# HG changeset patch # User Francisco J Ballesteros # Date 1331910061 -3600 # Node ID a23adee6c6a70c367b48d6824d9a4e3757287fc6 # Parent 5ff7711591da17138223c5415c031f7edc2b4cc8 creepy: fixes and rewrites for robustness. R=nixiedev, quanstro, nemo CC=nix-dev http://codereview.appspot.com/5795045 diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/9p.c --- a/sys/src/cmd/creepy/9p.c Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/9p.c Fri Mar 16 16:01:01 2012 +0100 @@ -13,12 +13,10 @@ #include "net.h" #include "fns.h" - /* * 9p server for creepy */ - static void rflush(Rpc*), rversion(Rpc*), rauth(Rpc*), rattach(Rpc*), rwalk(Rpc*), ropen(Rpc*), rcreate(Rpc*), @@ -42,20 +40,20 @@ [Twstat] rwstat, }; -void -ninestats(int clr) +char* +ninestats(char *s, char *e, int clr) { int i; - fprint(2, "fids:\t%4uld alloc %4uld free (%4uld bytes)\n", + s = seprint(s, e, "fids:\t%4uld alloc %4uld free (%4uld bytes)\n", fidalloc.nalloc, fidalloc.nfree, fidalloc.elsz); - fprint(2, "rpcs:\t%4uld alloc %4uld free (%4uld bytes)\n", + s = seprint(s, e, "rpcs:\t%4uld alloc %4uld free (%4uld bytes)\n", rpcalloc.nalloc, rpcalloc.nfree, rpcalloc.elsz); - fprint(2, "clis:\t%4uld alloc %4uld free (%4uld bytes)\n", + s = seprint(s, e, "clis:\t%4uld alloc %4uld free (%4uld bytes)\n", clialloc.nalloc, clialloc.nfree, clialloc.elsz); for(i = 0; i < nelem(fcalls); i++) if(fcalls[i] != nil && ncalls[i] > 0){ - fprint(2, "%-8s\t%5uld calls\t%11ulld µs\n", + s = seprint(s, e, "%-8s\t%5uld calls\t%11ulld µs per call\n", callname[i], ncalls[i], (calltime[i]/ncalls[i])/1000); if(clr){ @@ -63,24 +61,24 @@ calltime[i] = 0; } } + return s; } - static Qid mkqid(Memblk *f) { Qid q; - q.path = f->mf->id; - q.vers = f->mf->mtime; + q.path = f->d.id; + q.vers = f->d.mtime; q.type = 0; - if(f->mf->mode&DMDIR) + if(f->d.mode&DMDIR) q.type |= QTDIR; - if(f->mf->mode&DMTMP) + if(f->d.mode&DMTMP) q.type |= QTTMP; - if(f->mf->mode&DMAPPEND) + if(f->d.mode&DMAPPEND) q.type |= QTAPPEND; - if(f->mf->mode&DMEXCL) + if(f->d.mode&DMEXCL) q.type |= QTEXCL; if((q.type&QTEXCL) == 0) q.type |= QTCACHE; @@ -238,15 +236,15 @@ nulldir(&d); d.name = f->mf->name; d.qid = mkqid(f); - d.mode = f->mf->mode; - d.length = f->mf->length; + d.mode = f->d.mode; + d.length = f->d.length; if(d.mode&DMDIR) d.length = 0; d.uid = f->mf->uid; d.gid = f->mf->gid; d.muid = f->mf->muid; - d.atime = f->mf->atime; - d.mtime = f->mf->mtime; + d.atime = f->d.atime; + d.mtime = f->d.mtime / NSPERSEC; return convD2M(&d, buf, nbuf); } @@ -302,7 +300,7 @@ fid = getfid(rpc->cli, rpc->t.fid); rpc->fid = fid; if(catcherror()){ - d9print("clunking %X\n\n", fid); + dEprint("clunking %X:\n\t%r\n", fid); putfid(fid); putfid(fid); rpc->fid = nil; @@ -311,7 +309,7 @@ fidremove(fid); noerror(); - d9print("clunking %X\n\n", fid); + dEprint("clunking %X\n\n", fid); putfid(fid); putfid(fid); rpc->fid = nil; @@ -331,7 +329,7 @@ xqunlock(fid); error(nil); } - p = fid->p; + p = dflast(&fid->p, fid->p->nf); f = p->f[p->nf-1]; rwlock(f, Rd); noerror(); @@ -379,9 +377,11 @@ xqunlock(fid); error(nil); } + if(writedenied(fid->uid)) + error("user can't write"); p = fid->p; f = p->f[p->nf-1]; - if(fid->archived || f == fs->cons) + if(fid->archived || isro(f)) error("can't wstat archived or built-in files"); p = dfmelt(&fid->p, fid->p->nf); f = p->f[p->nf-1]; @@ -391,9 +391,14 @@ rwunlock(f, Wr); error(nil); } - if(sd.length != ~0 && sd.length != f->mf->length){ - if(f->mf->mode&DMDIR) + + if(f->d.mode&DMUSERS) + error("can't wstat the users file"); + if(sd.length != ~0 && sd.length != f->d.length){ + if(f->d.mode&DMDIR) error("can't resize a directory"); + if(sd.length != 0) + error("can't truncate to non-zero length"); dfaccessok(f, fid->uid, AWRITE); }else sd.length = ~0; @@ -403,48 +408,91 @@ error("can't rename built-in files"); dfaccessok(p->f[p->nf-2], fid->uid, AWRITE); if(!catcherror()){ - mbput(dfwalk(f, sd.name, 0)); + mbput(dfwalk(f, sd.name, Rd)); error("file already exists"); } }else sd.name[0] = 0; 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"); + if(!allowed(f->d.uid)){ + if(fid->uid != f->d.uid && !leader(f->d.gid, fid->uid)) + error("not the owner or group leader"); + if(!member(usrid(sd.uid), fid->uid) != 0) + error("you are not a member"); + } }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"); + /* + * Not std. in 9: leader must be member of the new gid, not + * leader of the new gid. + */ + if(!allowed(f->d.uid)){ + if(fid->uid != f->d.uid && !leader(f->d.gid, fid->uid)) + error("not the owner or group leader"); + if(!member(usrid(sd.gid), fid->uid) != 0) + error("you are not a member"); + } }else sd.gid[0] = 0; - if(sd.mode != ~0 && f->mf->mode != sd.mode){ - if(!fs->config && strcmp(fid->uid, f->mf->uid) != 0 && - !member(f->mf->gid, fid->uid) != 0) - error("only the onwer or members may change mode"); + + /* + * Not std. in 9: muid can be updated if uid is allowed, it's + * ignored otherwise. + */ + if(sd.muid[0] != 0 && strcmp(sd.muid, f->mf->muid) != 0){ + if(!allowed(f->d.uid)) + sd.muid[0] = 0; + }else + sd.muid[0] = 0; + + if(sd.mode != ~0 && f->d.mode != sd.mode){ + if((sd.mode&DMBITS) != sd.mode) + error("unknown bit set in mode"); + if(!allowed(f->d.uid)) + if(fid->uid != f->d.uid && !leader(f->d.gid, fid->uid)) + error("not the owner or group leader"); + if((sd.mode&DMDIR) ^ (f->d.mode&DMDIR)) + error("attempt to change DMDIR"); }else sd.mode = ~0; + /* + * Not std. in 9: allowed users can also set atime. + */ + if(sd.atime != ~0 && f->d.atime != sd.atime){ + if(!allowed(f->d.uid)) + sd.atime = ~0; /* ignore it */ + }else + sd.atime = ~0; + + if(sd.mtime != ~0 && f->d.mtime != sd.mtime){ + if(!allowed(f->d.uid)) + if(fid->uid != f->d.uid && !leader(f->d.gid, fid->uid)) + error("not the owner or group leader"); + }else + sd.mtime = ~0; + + /* + * Not std. in 9: other non-null fields, if any, are ignored. + */ if(sd.length != ~0) wstatint(f, "length", sd.length); if(sd.name[0]) wstatstr(f, "name", sd.name); if(sd.uid[0]) - wstatstr(f, "name", sd.name); + wstatstr(f, "uid", sd.uid); if(sd.gid[0]) - wstatstr(f, "name", sd.name); + wstatstr(f, "gid", sd.gid); + if(sd.muid[0]) + wstatstr(f, "muid", sd.muid); if(sd.mode != ~0) wstatint(f, "mode", sd.mode); - if(fs->config && sd.atime != ~0) + if(sd.atime != ~0) wstatint(f, "atime", sd.atime); - if(fs->config && sd.mtime != ~0) - wstatint(f, "mtime", sd.mtime); - if(fs->config && sd.muid[0] != 0 && strcmp(sd.muid, f->mf->muid) != 0) + if(sd.mtime != ~0) wstatint(f, "mtime", sd.mtime); noerror(); @@ -456,10 +504,10 @@ { Rpc *rpc; Cli *cli; - Fid *fid; char err[128]; long n; int nerr; + Memblk *fahead; rpc = v; cli = rpc->cli; @@ -473,32 +521,38 @@ nerr = nerrors(); + fspolicy(); + + rpc->r.tag = rpc->t.tag; rpc->r.type = rpc->t.type + 1; + quiescent(No); if(catcherror()){ + quiescent(Yes); rpc->r.type = Rerror; rpc->r.ename = err; rerrstr(err, sizeof err); }else{ - fcalls[rpc->t.type](rpc); + fcalls[rpc->t.type](rpc); + quiescent(Yes); noerror(); } - fid = nil; - if(rpc->fid != nil && rpc->fid->ref > 1){ - /* The fid is not clunked by this rpc; ok to read/walk ahead */ - fid = rpc->fid; - incref(fid); - } + xqlock(&cli->wlk); + fahead = nil; + if(rpc->fid != nil && rpc->fid->p != nil) + if(rpc->r.type == Rread || rpc->r.type == Rwalk){ + fahead = rpc->fid->p->f[rpc->fid->p->nf - 1]; + incref(fahead); + } if(catcherror()){ - if(fid != nil) - putfid(fid); + mbput(fahead); + error(nil); } - xqlock(&cli->wlk); putfid(rpc->fid); /* release rpc fid before replying */ - rpc->fid = nil; + rpc->fid = nil; /* or we might get "fid in use" errors */ if(rpc->flushed == 0){ d9print("-> %F\n", &rpc->r); @@ -513,18 +567,12 @@ ncalls[rpc->t.type]++; xqunlock(&cli->wlk); - if(fid != nil){ - switch(rpc->t.type){ - case Tread: /* read ahead? */ - if(rpc->r.type == Rread && rpc->r.count == rpc->t.count) - fidrahead(fid, rpc->t.offset + rpc->t.count); - break; - case Twalk: /* walk ahead? */ - if(rpc->r.type == Rwalk) - fidwahead(fid); - break; - } - putfid(fid); + if(fahead != nil){ + if(rpc->r.type == Rread) + rahead(fahead, rpc->t.offset + rpc->r.count); + else if(rpc->r.type == Rwalk) + wahead(fahead); + mbput(fahead); } noerror(); @@ -587,7 +635,6 @@ cli->nrpcs++; xqunlock(&cli->rpclk); - fspolicy(); if(rpc->t.type == Tflush || (Rpcspercli != 0 && cli->nrpcs >= Rpcspercli)) rpcworker9p(rpc, aux); @@ -600,5 +647,3 @@ dPprint("%s exiting\n", threadgetname()); return nil; }; - - diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/9pix.c --- a/sys/src/cmd/creepy/9pix.c Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/9pix.c Fri Mar 16 16:01:01 2012 +0100 @@ -65,7 +65,7 @@ fid->prev = nil; } -int +static int fidfmt(Fmt *fmt) { Fid *fid; @@ -127,7 +127,7 @@ n = 0; for(fid = fidshd; fid != nil; fid = fid->next) if(canqlock(fid)){ - if(!fid->archived) + if(!fid->archived && fid->p != nil) n += meltpath(fid->p); qunlock(fid); } @@ -223,11 +223,9 @@ if(fid == nil || decref(fid) > 0) return; - dEprint("clunk fid %X\n", fid); + d9print("clunk %X\n", fid); putpath(fid->p); fid->p = nil; - free(fid->uid); - fid->uid = nil; xrwlock(&fidhashlk, Wr); if(catcherror()){ xrwunlock(&fidhashlk, Wr); @@ -247,7 +245,7 @@ } /* keeps addr, does not copy it */ -Cli* +static Cli* newcli(char *addr, int fd, int cfd) { Cli *cli; @@ -291,7 +289,22 @@ { Path *p; - fid->uid = strdup(uname); + fid->uid = usrid(uname); + + /* + * The owner of the process must be able to attach, if only to + * define users and administer the file system. + */ + if(fid->uid < 0 && strcmp(uname, getuser()) == 0){ + fprint(2, "%s: owner uid '%s' unknown. attaching as 'elf'\n", + argv0, uname); + fid->uid = usrid("elf"); + } + + if(fid->uid < 0){ + fprint(2, "%s: unknown user '%s'. using 'none'\n", argv0, uname); + fid->uid = 1; + } p = newpath(fs->root); fid->p = p; if(strcmp(aname, "active") == 0 || strcmp(aname, "main/active") == 0){ @@ -317,14 +330,198 @@ } nfid = newfid(cli, no); nfid->p = clonepath(fid->p); - nfid->uid = strdup(fid->uid); + nfid->uid = fid->uid; nfid->archived = fid->archived; nfid->consopen = fid->consopen; + nfid->buf = fid->buf; noerror(); xqunlock(fid); return nfid; } +static Memblk* +pickarch(Memblk *d, uvlong t) +{ + Blksl sl; + daddrt *de; + uvlong off; + int i; + uvlong cmtime; + daddrt cdaddr; + Memblk *f; + + off = 0; + cmtime = 0; + cdaddr = 0; + for(;;){ + sl = dfslice(d, Dblkdatasz, off, Rd); + if(sl.len == 0){ + assert(sl.b == nil); + break; + } + if(sl.b == nil) + continue; + if(catcherror()){ + mbput(sl.b); + error(nil); + } + for(i = 0; i < sl.len/Daddrsz; i++){ + de = sl.data; + de += i; + if(*de == 0) + continue; + f = dbget(DBfile, *de); + if(f->d.mtime > cmtime && f->d.mtime < t){ + cmtime = f->d.mtime; + cdaddr = *de; + } + mbput(f); + } + noerror(); + mbput(sl.b); + off += sl.len; + } + if(cdaddr == 0) + error("file not found"); + return dbget(DBfile, cdaddr); +} + +static int +digs(char **sp, int n) +{ + char b[8]; + char *s; + + s = *sp; + if(strlen(s) < n) + return 0; + assert(n < sizeof b - 1); + strecpy(b, b+n+1, *sp); + *sp += n; + return strtoul(b, nil, 10); +} + +/* + * convert symbolic time into a valid /archive wname. + * yyyymmddhhmm, yyyymmdd, mmdd, or hh:mm + */ +static Memblk* +archwalk(Memblk *f, char *wname) +{ + char *s; + Tm *tm; + uvlong t; + static QLock tmlk; + int wl; + + s = wname; + qlock(&tmlk); /* localtime is not reentrant! */ + tm = localtime(time(nil)); + wl = strlen(wname); + switch(wl){ + case 12: /* yyyymmddhhmm */ + case 8: /* yyyymmdd */ + tm->year = digs(&s, 4) - 1900; + case 4: /* mmdd */ + tm->mon = digs(&s, 2) - 1; + tm->mday = digs(&s, 2); + if(wl == 8) + break; + /* else fall */ + case 5: /* hh:mm */ + tm->hour = digs(&s, 2); + if(wl == 5 && s[0] != 0) + s++; + tm->min = digs(&s, 2); + break; + default: + qunlock(&tmlk); + error("file not found"); + } + dprint("archwalk to %d/%d/%d %d:%d:%d\n", + tm->year, tm->mday, tm->mon, tm->hour, tm->min, tm->sec); + t = tm2sec(tm); + t *= NSPERSEC; + qunlock(&tmlk); + return pickarch(f, t); +} + + +/* + * We are at /active/... or /archive/x/... + * Walk to /archive/T/... and return it so it's added to the + * path by the caller. + */ +static Memblk* +timewalk(Path *p, char *wname, int uid) +{ + Memblk *f, *nf, *pf, *arch, *af; + int i, isarch; + + if(p->nf < 2 || (p->f[1] == fs->archive && p->nf < 3)) + error("file not found"); + assert(p->f[0] == fs->root); + isarch = p->f[1] == fs->archive; + assert(p->f[1] == fs->active || isarch); + + arch = fs->archive; + rwlock(arch, Rd); + if(catcherror()){ + rwunlock(arch, Rd); + error(nil); + } + af = archwalk(arch, wname); + noerror(); + rwunlock(arch, Rd); + if(catcherror()){ + mbput(af); + error(nil); + } + i = 2; + if(isarch) + i++; + f = af; + incref(f); + nf = nil; + for(; i < p->nf; i++){ + pf = p->f[i]; + rwlock(f, Rd); + rwlock(pf, Rd); + if(catcherror()){ + rwunlock(pf, Rd); + rwunlock(f, Rd); + mbput(f); + error(nil); + } + dfaccessok(f, uid, AEXEC); + nf = dfwalk(f, pf->mf->name, Rd); + noerror(); + rwunlock(pf, Rd); + rwunlock(f, Rd); + mbput(f); + f = nf; + } + noerror(); + mbput(af); + USED(f); + USED(nf); + + if((f->d.mode&DMDIR) == 0){ /* it was not a dir at that time! */ + mbput(f); + error("file not found"); + } + return f; +} + +/* + * For walks into /archive, wname may be any time value (ns) or + * yyyymmddhhmm, yyyymmdd, mmdd, or hh:mm, + * and the walk proceeds to the archived file + * with the bigger mtime not greater than the specified time. + * If there's no such time, walk reports a file not found error. + * + * walks using @ lead to the corresponding dir in the archive. + */ void fidwalk(Fid *fid, char *wname) { @@ -336,7 +533,7 @@ xqunlock(fid); error(nil); } - p = fid->p; + p = dflast(&fid->p, fid->p->nf); if(strcmp(wname, ".") == 0) goto done; if(strcmp(wname, "..") == 0){ @@ -351,7 +548,21 @@ error(nil); } dfaccessok(f, fid->uid, AEXEC); - nf = dfwalk(f, wname, 0); + + nf = nil; + if(catcherror()){ + if(f == fs->archive) + nf = archwalk(f, wname); + else if(wname[0] == '@' && f != fs->cons && f != fs->stats) + nf = timewalk(p, wname+1, fid->uid); + else + error(nil); + fid->archived = 1; + }else{ + nf = dfwalk(f, wname, Rd); + noerror(); + } + rwunlock(f, Rd); noerror(); p = addelem(&fid->p, nf); @@ -359,9 +570,10 @@ done: f = p->f[p->nf-1]; if(isro(f)) - fid->archived = f != fs->cons; + fid->archived = f != fs->cons && f != fs->stats; else if(f == fs->active) fid->archived = 0; + dfused(p); noerror(); xqunlock(fid); } @@ -385,9 +597,12 @@ } p = fid->p; f = p->f[p->nf-1]; - if(mode != OREAD) + if(mode != OREAD){ if(f == fs->root || f == fs->archive || fid->archived) error("can't write archived or built-in files"); + if(writedenied(fid->uid)) + error("user can't write"); + } amode = 0; if((mode&3) != OREAD || (mode&OTRUNC) != 0) amode |= AWRITE; @@ -400,34 +615,47 @@ p = dfmelt(&fid->p, fid->p->nf); f = p->f[p->nf-1]; } - else + else{ + p = dflast(&fid->p, fid->p->nf); rwlock(f, Rd); + } if(catcherror()){ rwunlock(f, (amode!=AREAD)?Wr:Rd); error(nil); } - fmode = f->mf->mode; + fmode = f->d.mode; if(mode != OREAD){ - if(f != fs->root && p->f[p->nf-2]->mf->mode&DMAPPEND) + if(f != fs->root && p->f[p->nf-2]->d.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) + if(fid->archived || isro(f)) error("can't remove an archived or built-in file"); + if(f->d.mode&DMUSERS) + error("can't remove /users"); dfaccessok(p->f[p->nf-2], fid->uid, AWRITE); + fid->rclose++; } - 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){ + if((mode&OTRUNC) != 0&& f != fs->cons && f != fs->stats){ z = 0; dfwattr(f, "length", &z, sizeof z); + if(f->d.mode&DMUSERS){ + f->d.mode = 0664|DMUSERS; + f->d.uid = usrid("adm"); + f->d.gid = usrid("adm"); + f->mf->uid = "adm"; + f->mf->gid = "adm"; + changed(f); + } } + if(f == fs->stats) + fid->buf = updatestats(mode&OTRUNC); f->mf->open++; fid->omode = mode&3; fid->loff = 0; @@ -436,7 +664,9 @@ noerror(); rwunlock(f, (amode!=AREAD)?Wr:Rd); if(mode&OTRUNC) - dfchanged(p); + dfchanged(p, fid->uid); + else + dfused(p); noerror(); xqunlock(fid); } @@ -460,11 +690,18 @@ error("that file name is too creepy"); if((perm&DMDIR) != 0 && mode != OREAD) error("wrong open mode for a directory"); + if(writedenied(fid->uid)) + error("user can't write"); + if(fid->archived) + error("%P is archived or builtin", fid->p); + if((perm&DMBITS) != perm) + error("unknown bit set in perm %M %#ulx", perm, perm); 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) + if(mode&ORCLOSE) + if(f->d.mode&DMUSERS) + error("can't remove the users file"); + if((f->d.mode&DMDIR) == 0) error("not a directory"); p = dfmelt(&fid->p, fid->p->nf); f = p->f[p->nf-1]; @@ -474,11 +711,21 @@ } dfaccessok(f, fid->uid, AWRITE); if(!catcherror()){ - mbput(dfwalk(f, name, 0)); + mbput(dfwalk(f, name, Rd)); error("file already exists"); } + + nf = dfcreate(f, name, fid->uid, perm); p = addelem(&fid->p, nf); + if(f == fs->active && strcmp(name, "users") == 0){ + nf->d.mode = 0664|DMUSERS; + nf->d.uid = usrid("adm"); + nf->d.gid = usrid("adm"); + nf->mf->uid = "adm"; + nf->mf->gid = "adm"; + changed(nf); + } decref(nf); nf->mf->open++; noerror(); @@ -488,7 +735,7 @@ fid->lidx = 0; if(mode&ORCLOSE) fid->rclose++; - dfchanged(p); + dfchanged(p, fid->uid); noerror(); xqunlock(fid); } @@ -513,6 +760,23 @@ return tot; } +static long +strread(char *s, void *data, ulong count, vlong offset) +{ + long n; + + n = strlen(s); + if(offset >= n) + return 0; + s += offset; + n -= offset; + if(count > n) + count = n; + if(count > 0) + memmove(data, s, count); + return count; +} + long fidread(Fid *fid, void *data, ulong count, vlong offset, Packmeta pack) { @@ -530,13 +794,20 @@ error("fid not open for reading"); if(offset < 0) error("negative offset"); - p = fid->p; + p = dflast(&fid->p, fid->p->nf); f = p->f[p->nf-1]; if(f == fs->cons){ noerror(); xqunlock(fid); return consread(data, count); } + if(f == fs->stats){ + noerror(); + assert(fid->buf != nil); + count = strread(fid->buf, data, count, offset); + xqunlock(fid); + return count; + } rwlock(f, Rd); noerror(); xqunlock(fid); @@ -544,7 +815,7 @@ rwunlock(f, Rd); error(nil); } - if(f->mf->mode&DMDIR){ + if(f->d.mode&DMDIR){ if(fid->loff != offset) error("non-sequential dir read not supported"); count = readdir(fid, data, count, offset, pack); @@ -553,6 +824,7 @@ count = dfpread(f, data, count, offset); noerror(); rwunlock(f, Rd); + dfused(p); return count; } @@ -567,6 +839,8 @@ xqunlock(fid); error(nil); } + if(writedenied(fid->uid)) + error("user can't write"); if(fid->omode == -1) error("fid not open"); if(fid->omode == OREAD) @@ -578,6 +852,12 @@ noerror(); return conswrite(data, count); } + if(f == fs->stats){ + xqunlock(fid); + noerror(); + return count; + } + p = dfmelt(&fid->p, fid->p->nf); f = p->f[p->nf-1]; if(catcherror()){ @@ -588,7 +868,7 @@ noerror(); rwunlock(f, Wr); - dfchanged(p); + dfchanged(p, fid->uid); noerror(); xqunlock(fid); return count; @@ -609,9 +889,12 @@ f = p->f[p->nf-1]; rwlock(f, Wr); f->mf->open--; + if((f->d.mode&DMUSERS) && (fid->omode&3) != OREAD) + rwusers(f); rwunlock(f, Wr); fid->omode = -1; if(fid->rclose){ + dflast(&fid->p, fid->p->nf); p = dfmelt(&fid->p, fid->p->nf-1); fp = p->f[p->nf-2]; rwlock(f, Wr); @@ -624,7 +907,7 @@ noerror(); } rwunlock(fp, Wr); - dfchanged(p); + dfchanged(p, fid->uid); } putpath(fid->p); fid->p = nil; @@ -644,10 +927,13 @@ xqunlock(fid); error(nil); } + if(writedenied(fid->uid)) + error("user can't write"); p = fid->p; f = p->f[p->nf-1]; - if(fid->archived || f == fs->cons || f == fs->active) + if(fid->archived || isro(f)) error("can't remove archived or built-in files"); + dflast(&fid->p, fid->p->nf); p = dfmelt(&fid->p, fid->p->nf-1); fp = p->f[p->nf-2]; f = p->f[p->nf-1]; @@ -657,15 +943,17 @@ rwunlock(fp, Wr); error(nil); } - if(fp->mf->mode&DMAPPEND) + if(fp->d.mode&DMAPPEND) error("directory is append only"); + if(f->d.mode&DMUSERS) + error("can't remove the users file"); dfaccessok(fp, fid->uid, AWRITE); fid->omode = -1; dfremove(fp, f); fid->p->nf--; noerror(); rwunlock(fp, Wr); - dfchanged(fid->p); + dfchanged(fid->p, fid->uid); putpath(fid->p); fid->p = nil; noerror(); @@ -699,22 +987,11 @@ * that's ok. */ void -fidrahead(Fid *fid, uvlong offset) +rahead(Memblk *f, uvlong offset) { - Path *p; - Memblk *f; Mfile *m; - xqlock(fid); - if(catcherror()){ - xqunlock(fid); - error(nil); - } - p = fid->p; - f = p->f[p->nf-1]; rwlock(f, Rd); - xqunlock(fid); - noerror(); m = f->mf; if(m->sequential == 0 || m->raoffset > offset + Nahead){ rwunlock(f, Rd); @@ -722,7 +999,8 @@ } if(catcherror()){ rwunlock(f, Rd); - error(nil); + fprint(2, "%s: rahead: %r\n", argv0); + return; } m->raoffset = offset + Nahead; d9print("rahead d%#ullx off %#ullx\n", f->addr, m->raoffset); @@ -740,36 +1018,23 @@ * loaded in memory before further walks/reads. */ void -fidwahead(Fid *fid) +wahead(Memblk *f) { - Path *p; - Memblk *f; Mfile *m; int i; - xqlock(fid); - if(catcherror()){ - xqunlock(fid); - error(nil); + rwlock(f, Rd); + m = f->mf; + if((f->d.mode&DMDIR) == 0 || m->wadone){ + rwunlock(f, Rd); + return; } - p = fid->p; - f = p->f[p->nf-1]; - rwlock(f, Rd); - noerror(); - xqunlock(fid); - + m->wadone = 1; if(catcherror()){ rwunlock(f, Rd); error(nil); } - m = f->mf; - if((m->mode&DMDIR) == 0 || m->wadone){ - noerror(); - rwunlock(f, Rd); - return; - } - m->wadone = 1; - for(i = 0; i < f->mf->length/sizeof(Dentry); i++) + for(i = 0; i < f->d.length/Daddrsz; i++) mbput(dfchild(f, i)); noerror(); rwunlock(f, Rd); @@ -816,7 +1081,7 @@ return sys; } -void +static void srv9pix(char *srv, char* (*cliworker)(void *arg, void **aux)) { Cli *cli; @@ -832,7 +1097,7 @@ getworker(cliworker, cli, nil); } -void +static void listen9pix(char *addr, char* (*cliworker)(void *arg, void **aux)) { Cli *cli; @@ -879,8 +1144,9 @@ break; default: if(ARGC() >= 'A' && ARGC() <= 'Z' || ARGC() == '9'){ - dbg['D'] = 1; + dbg['d'] = 1; dbg[ARGC()] = 1; + fatalaborts = 1; }else usage(); }ARGEND; @@ -895,22 +1161,20 @@ fmtinstall('G', ixcallfmt); fmtinstall('X', fidfmt); fmtinstall('R', rpcfmt); + fmtinstall('A', usrfmt); + fmtinstall('P', pathfmt); + errinit(Errstack); if(catcherror()) fatal("uncatched error: %r"); rfork(RFNAMEG); - parseusers(defaultusers); + rwusers(nil); fsopen(dev); if(srv != nil) srv9pix(srv, cliworker9p); if(addr != nil) listen9pix(addr, cliworker9p); - /* - * fsstats(); - * ninestats(); - * ixstats(); - */ consinit(); noerror(); threadexits(nil); diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/attr.c --- a/sys/src/cmd/creepy/attr.c Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/attr.c Fri Mar 16 16:01:01 2012 +0100 @@ -16,8 +16,7 @@ * 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; @@ -34,10 +33,12 @@ long wname(Memblk*, void*, long); static long rname(Memblk*, void*, long); static long rid(Memblk*, void*, long); -long wid(Memblk*, void*, long); -long watime(Memblk*, void*, long); +static long wid(Memblk*, void*, long); +static long wmode(Memblk*, void*, long); +static long rmode(Memblk*, void*, long); +static long watime(Memblk*, void*, long); static long ratime(Memblk*, void*, long); -long wmtime(Memblk*, void*, long); +static long wmtime(Memblk*, void*, long); static long rmtime(Memblk*, void*, long); static long wlength(Memblk*, void*, long); static long rlength(Memblk*, void*, long); @@ -61,6 +62,7 @@ {"uid", 0, wuid, ruid, cstring}, {"gid", 0, wgid, rgid, cstring}, {"muid", 0, wuid, ruid, cstring}, + {"mode", BIT64SZ, wmode, rmode, cu64int}, {"*", 0, nil, rstar, nil}, }; @@ -85,58 +87,28 @@ } void -gmeta(Fmeta *meta, void *buf, ulong nbuf) +gmeta(Memblk *f, void *buf, ulong nbuf) { - Dmeta *d; - char *p, *x; + char *p; int i; - if(nbuf < sizeof *d) - error("metadata buffer too small"); - d = buf; - meta->id = d->id; - meta->mode = d->mode; - meta->atime = d->atime; - meta->mtime = d->mtime; - meta->length = d->length; - - if(d->ssz[FMuid] + sizeof *d > nbuf || - d->ssz[FMgid] + sizeof *d > nbuf || - d->ssz[FMmuid] + sizeof *d > nbuf || - d->ssz[FMname] + sizeof *d > nbuf) - error("corrupt meta: wrong string size"); - - p = (char*)(&d[1]); - x = p; - for(i = 0; i < nelem(d->ssz); i++){ - if(x[d->ssz[i]-1] != 0) - error("corrupt meta: unterminated string"); - x += d->ssz[i]; - } - - meta->uid = p; - p += d->ssz[FMuid]; - meta->gid = p; - p += d->ssz[FMgid]; - meta->muid = p; - p += d->ssz[FMmuid]; - meta->name = p; + f->mf->uid = usrname(f->d.uid); + f->mf->gid = usrname(f->d.gid); + f->mf->muid = usrname(f->d.muid); + p = buf; + for(i = 0; i < nbuf; i++) + if(p[i] == 0) + break; + if(i == nbuf) + error("corrupt meta"); + f->mf->name = buf; + } static ulong -metasize(Fmeta *meta) +metasize(Memblk *f) { - ulong n; - - n = sizeof(Dmeta); - n += strlen(meta->uid) + 1; - n += strlen(meta->gid) + 1; - n += strlen(meta->muid) + 1; - n += strlen(meta->name) + 1; - /* - * BUG: meta->attr - */ - return n; + return strlen(f->mf->name) + 1; } /* @@ -146,45 +118,22 @@ * The caller is responsible for ensuring that metadata fits in buf. */ ulong -pmeta(void *buf, ulong nbuf, Fmeta *meta) +pmeta(void *buf, ulong nbuf, Memblk *f) { - Dmeta *d; - char *p, *bufp; + char *p, *e; ulong sz; - sz = metasize(meta); + sz = metasize(f); if(sz > nbuf) error("attributes are too long"); - d = buf; - bufp = buf; - d->id = meta->id; - d->mode = meta->mode; - d->atime = meta->atime; - d->mtime = meta->mtime; - d->length = meta->length; - - p = (char*)(&d[1]); - d->ssz[FMuid] = strlen(meta->uid) + 1; - strcpy(p, meta->uid); - meta->uid = p; - p += d->ssz[FMuid]; - - d->ssz[FMgid] = strlen(meta->gid) + 1; - strcpy(p, meta->gid); - meta->gid = p; - p += d->ssz[FMgid]; - - d->ssz[FMmuid] = strlen(meta->muid) + 1; - strcpy(p, meta->muid); - meta->muid = p; - p += d->ssz[FMmuid]; - - d->ssz[FMname] = strlen(meta->name) + 1; - strcpy(p, meta->name); - meta->name = p; - p += d->ssz[FMname]; - - assert(p - bufp <= sz); /* can be <, to leave room for growing */ + p = buf; + if(f->mf->name != p) + e = strecpy(p, p+nbuf, f->mf->name); + else + e = p + strlen(p); + e++; + assert(e-p <= sz); /* can be <, to leave room for growing */ + f->mf->name = p; return sz; } @@ -260,13 +209,12 @@ old = f->mf->name; f->mf->name = p; maxsz = embedattrsz(f); - if(metasize(f->mf) > maxsz){ + if(metasize(f) > maxsz){ f->mf->name = old; - fprint(2, "%s: bug: no attribute block implemented\n", argv0); + fprint(2, "%s: bug: can't handle DBattr blocks\n", argv0); error("no room to grow metadata"); } - /* name goes last, we can pack in place */ - pmeta(f->d.embed, maxsz, f->mf); + pmeta(f->d.embed, maxsz, f); return len; } @@ -294,31 +242,25 @@ return rstr(f->mf->uid, buf, len); } -static long -wuid(Memblk *f, void *buf, long len) +static u64int +chkusr(void *buf, long len) { char *p; - ulong maxsz; - Fmeta m; + int id; 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); + id = usrid(buf); + if(id < 0) + error("unknown user '%s'", buf); + return id; +} + +static long +wuid(Memblk *f, void *buf, long len) +{ + f->d.uid = chkusr(buf, len); return len; } @@ -331,28 +273,7 @@ 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); + f->d.gid = chkusr(buf, len); return len; } @@ -365,41 +286,17 @@ 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); + f->d.muid = chkusr(buf, len); return len; } -long +static long wid(Memblk *f, void *buf, long) { u64int *p; - Dmeta *d; p = buf; - d = (Dmeta*)f->d.embed; - f->mf->id = *p; - d->id = *p; + f->d.id = *p; return BIT64SZ; } @@ -409,20 +306,17 @@ u64int *p; p = buf; - *p = f->mf->id; + *p = f->d.id; return BIT64SZ; } -long +static long watime(Memblk *f, void *buf, long) { u64int *p; - Dmeta *d; p = buf; - d = (Dmeta*)f->d.embed; - f->mf->atime = *p; - d->atime = *p; + f->d.atime = *p; return BIT64SZ; } @@ -432,20 +326,37 @@ u64int *p; p = buf; - *p = f->mf->atime; + *p = f->d.atime; return BIT64SZ; } -long +static long +wmode(Memblk *f, void *buf, long) +{ + u64int *p; + + p = buf; + f->d.mode = *p | (f->d.mode&DMUSERS); + return BIT64SZ; +} + +static long +rmode(Memblk *f, void *buf, long) +{ + u64int *p; + + p = buf; + *p = f->d.mode & ~DMUSERS; + return BIT64SZ; +} + +static long wmtime(Memblk *f, void *buf, long) { u64int *p; - Dmeta *d; p = buf; - d = (Dmeta*)f->d.embed; - f->mf->mtime = *p; - d->mtime = *p; + f->d.mtime = *p; return BIT64SZ; } @@ -455,21 +366,21 @@ u64int *p; p = buf; - *p = f->mf->mtime; + *p = f->d.mtime; return BIT64SZ; } static uvlong -fresize(Memblk *f, uvlong sz) +resized(Memblk *f, uvlong sz) { ulong boff, bno, bend; - if(f->mf->mode&DMDIR) + if(f->d.mode&DMDIR) error("can't resize a directory"); if(sz > maxfsz) error("max file size exceeded"); - if(sz >= f->mf->length) + if(sz >= f->d.length) return sz; bno = dfbno(f, sz, &boff); if(boff > 0) @@ -485,12 +396,10 @@ wlength(Memblk *f, void *buf, long) { u64int *p; - Dmeta *d; p = buf; - d = (Dmeta*)f->d.embed; - f->mf->length = fresize(f, *p); - d->length = *p; + f->d.length = *p; + resized(f, f->d.length); return BIT64SZ; } @@ -500,7 +409,7 @@ u64int *p; p = buf; - *p = f->mf->length; + *p = f->d.length; return BIT64SZ; } @@ -567,6 +476,9 @@ return tot; } +/* + * cond on attribute value + */ void dfcattr(Memblk *f, int op, char *name, void *val, long count) { @@ -589,25 +501,31 @@ adef[i].cattr(f, op, buf, nbuf, val, count); } +/* + * Does not check if the user can't write because of the "write" + * user. + * Does check if the user is allowed in config mode. + */ void -dfaccessok(Memblk *f, char *uid, int bits) +dfaccessok(Memblk *f, int uid, int bits) { uint mode; - if(fs->config) + if(allowed(uid)) return; bits &= 3; - mode = f->mf->mode &0777; + + mode = f->d.mode &0777; if((mode&bits) == bits) return; mode >>= 3; - if(member(f->mf->gid, uid) && (mode&bits) == bits) + if(member(f->d.gid, uid) && (mode&bits) == bits) return; mode >>= 3; - if(strcmp(f->mf->uid, uid) == 0 && (mode&bits) == bits) + if(f->d.uid == uid && (mode&bits) == bits) return; error("permission denied"); } diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/cfg.c --- a/sys/src/cmd/creepy/cfg.c Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/cfg.c Fri Mar 16 16:01:01 2012 +0100 @@ -13,52 +13,56 @@ #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. + * Locking is coarse, only functions used from outside + * care to lock the user information. * - * sorry. 9 rules were too complex to remember. + * Access checks are like those described in Plan 9's stat(5), but for: + * + * - to change gid, the group leader is not required to be a leader + * of the new group; it suffices if he's a member. + * - attempts to change muid are honored if the user is "allowed", + * and ignored otherwise. + * - "allowed" users can also set atime. + * - attributes other than length, name, uid, gid, muid, mode, atime, and mtime + * are ignored (no error raised if they are not void) + * + * + * The user file has the format: + * uid:name:leader:members + * uid is a number. + * + * This program insists on preserving uids already seen. + * That is, after editing /active/adm/users, the server program will notice + * and re-read the file, then clean it up, and upate its contents. + * + * Cleaning ensures that uids for known users are kept as they were, and + * that users not yet seen get unique uids. Numeric uids are only an internal + * concept, the protocol uses names. */ -typedef struct Usr Usr; -typedef struct Member Member; +/* + * The uid numbers are irrelevant, they are rewritten. + * XXX: There's code assuming the positions of these users... + * search for it and make it search the user if needed or use a global for it. + */ +static char *defaultusers = + "1:none::\n" + "2:adm:adm:sys, elf \n" + "3:sys::glenda,elf\n" + "4:glenda:glenda:\n" + "5:elf:elf:sys\n"; -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 RWLock ulk; +static Usr *uids[Uhashsz]; +static Usr *unames[Uhashsz]; +static Usr *uwrite; +static int uidgen; static uint -uhash(char* s) +usrhash(char* s) { uchar *p; - u32int hash; + uint hash; hash = 0; for(p = (uchar*)s; *p != '\0'; p++) @@ -67,129 +71,269 @@ return hash % Uhashsz; } -static Usr* -findusr(char *uid) +static int +findmember(Usr *u, int member) { - 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) + if(member == m->u->id) return 1; return 0; } static Usr* -mkusr(char *uid, char *lead) +finduid(int uid) +{ + Usr *u; + + for(u = uids[uid%Uhashsz]; u != nil; u = u->inext) + if(u->id == uid) + return u; + return nil; +} + +static Usr* +finduname(char *name, int mkit) { 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); + h = usrhash(name); + for(u = unames[h]; u != nil; u = u->nnext) + if(strcmp(u->name, name) == 0) + return u; + if(mkit){ + /* might be leaked. see freeusr() */ + u = mallocz(sizeof *u, 1); + strecpy(u->name, u->name+sizeof u->name, name); + u->nnext = unames[h]; + unames[h] = u; + } + return u; +} - 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; +char* +usrname(int uid) +{ + Usr *u; + + xrwlock(&ulk, Rd); + u = finduid(uid); + if(u == nil){ + xrwunlock(&ulk, Rd); /* zero patatero: */ + return "ZP"; /* disgusting, isn't it? */ + } + xrwunlock(&ulk, Rd); + return u->name; +} + +int +usrid(char *n) +{ + Usr *u; + + xrwlock(&ulk, Rd); + u = finduname(n, Dontmk); + if(u == nil || !u->enabled){ + xrwunlock(&ulk, Rd); + return -1; + } + xrwunlock(&ulk, Rd); + return u->id; +} + +int +member(int uid, int member) +{ + Usr *u; + int r; + + if(uid == member) + return 1; + xrwlock(&ulk, Rd); + u = finduid(uid); + r = u != nil && u->lead != nil && u->lead->id == member; + r |= u != nil && findmember(u, member); + xrwunlock(&ulk, Rd); + return r; +} + +int +leader(int gid, int lead) +{ + Usr *u; + int r; + + xrwlock(&ulk, Rd); + u = finduid(gid); + r = 0; + if(u != nil) + if(u->lead != nil) + r = u->lead->id == lead; + else + r = findmember(u, lead); + xrwunlock(&ulk, Rd); + return r; +} + +static void +clearmembers(Usr *u) +{ + Member *m; + + while(u->members != nil){ + m = u->members; + u->members = m->next; + free(m); + } } static void addmember(Usr *u, char *n) { - Member *m; + Member *m, **ml; - 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); + for(ml = &u->members; (m = *ml) != nil; ml = &m->next) + if(strcmp(m->u->name, n) == 0){ + xrwunlock(&ulk, Wr); + fprint(2, "%s: '%s' is already a member of '%s'", + argv0, n, u->name); + return; + } 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); - } + m->u = finduname(n, Mkit); + *ml = m; } static void checkmembers(Usr *u) { + Member *m, **ml; + + for(ml = &u->members; (m = *ml) != nil; ) + if(m->u->id == 0){ + fprint(2, "no user '%s' (member of '%s')\n", + m->u->name, u->name); + *ml = m->next; + free(m); + }else + ml = &m->next; +} + +int +usrfmt(Fmt *fmt) +{ + Usr *usr; 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); + usr = va_arg(fmt->args, Usr*); + + if(usr == nil) + return fmtprint(fmt, "#no user"); + fmtprint(fmt, "%s%d:%s:", usr->enabled?"":"!", + usr->id, usr->name); + fmtprint(fmt, "%s:", usr->lead?usr->lead->name:""); + for(m = usr->members; m != nil; m = m->next){ + fmtprint(fmt, "%s", m->u->name); + if(m->next != nil) + fmtprint(fmt, ","); + } + return 0; +} + +static void +dumpusers(void) +{ + int i; + Usr *usr; + + for(i = 0; i < nelem(uids); i++) + for(usr = uids[i]; usr != nil; usr = usr->inext){ + fprint(2, "%A\n", usr); } } -void -parseusers(char *u) +/* + * Add a user. + * A partial user entry might already exists, as a placeholder + * for the user name (if seen before in the file). + * If the user was known, it's uid is preserved. + * If not, a new unique uid is assigned. + */ +static Usr* +mkusr(char *name) { - char *c, *nc, *p, *np, *args[5]; - int nargs, i; + Usr *u; + uint h; + + u = finduname(name, Mkit); + if(u->id == 0){ + /* first seen! */ + u->id = ++uidgen; + h = u->id%Uhashsz; + u->inext = uids[h]; + uids[h] = u; + } + if(strcmp(name, "write") == 0) + uwrite = u; + return u; +} + +static void +addusr(char *p) +{ + char *c, *nc, *s, *args[5]; + int nargs, on; Usr *usr; - u = strdup(u); + on = 1; + if(*p == '!'){ + on = 0; + p++; + } + nargs = getfields(p, args, nelem(args), 0, ":"); + if(nargs != 4) + error("wrong number of fields %s", args[0]); + if(*args[1] == 0) + error("null name"); + usr = mkusr(args[1]); + usr->enabled = on; + usr->lead = finduname(args[2], Mkit); + clearmembers(usr); + for(c = args[3]; c != nil; c = nc){ + while(*c == ' ' || *c == '\t') + c++; + if(*c == 0) + break; + nc = utfrune(c, ','); + if(nc != nil) + *nc++ = 0; + s = utfrune(c, ' '); + if(s != nil) + *s = 0; + s = utfrune(c, '\t'); + if(s != nil) + *s = 0; + if(*c != 0) + addmember(usr, c); + } +} + +/* + * Absorb the new user information as read from u. + * Old users are not removed, but renamed to be disabled. + */ +static void +rwdefaultusers(void) +{ + char *u, *c, *p, *np; + static int once; + + if(once++ > 0) + return; + + u = strdup(defaultusers); if(catcherror()){ free(u); error(nil); @@ -202,34 +346,132 @@ c = utfrune(p, '#'); if(c != nil) *c = 0; + if(*p == 0) + continue; 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); - } + addusr(p); noerror(); }while((p = np) != nil); - for(i = 0; i < nelem(usrs); i++) - for(usr = usrs[i]; usr != nil; usr = usr->next) - checkmembers(usr); + + if(dbg['d']){ + dprint("users:\n"); + dumpusers(); + dprint("\n"); + } noerror(); free(u); + +} + +/* + * This should be called at start time and whenever + * the user updates /active/adm/users, to rewrite it according to our + * in memory data base. + */ +void +rwusers(Memblk *uf) +{ + static char ubuf[512]; + char *p, *nl, *c; + uvlong off; + u64int z; + long tot, nr, nw; + int i; + Usr *usr; + + xrwlock(&ulk, Wr); + if(catcherror()){ + fprint(2, "%s: users: %r\n", argv0); + goto update; + } + if(uf == nil){ + rwdefaultusers(); + xrwunlock(&ulk, Wr); + return; + } + tot = 0; + p = nil; + for(off = 0; off < uf->d.length; off += nr){ + nr = dfpread(uf, ubuf + tot, sizeof ubuf - tot - 1, off); + tot += nr; + ubuf[tot] = 0; + for(p = ubuf; p != nil && p - ubuf < tot; p = nl){ + nl = utfrune(p, '\n'); + if(nl == nil){ + tot = strlen(p); + memmove(ubuf, p, tot+1); + break; + } + *nl++ = 0; + c = utfrune(p, '#'); + if(c != nil) + *c = 0; + if(*p != 0) + addusr(p); + } + } + if(p != nil) + fprint(2, "%s: last line in users is not a full line\n", argv0); + + noerror(); + if(uf->frozen){ /* loaded at boot time */ + xrwunlock(&ulk, Wr); + return; + } + +update: + if(catcherror()){ + xrwunlock(&ulk, Wr); + fprint(2, "%s: users: %r\n", argv0); + return; /* what could we do? */ + } + ismelted(uf); + isrwlocked(uf, Wr); + z = 0; + dfwattr(uf, "length", &z, sizeof z); + off = 0; + dprint("users updated:\n"); + for(i = 0; i < uidgen; i++) + if((usr=finduid(i)) != nil){ + dprint("%A\n", usr); + p = seprint(ubuf, ubuf+sizeof ubuf, "%A\n", usr); + nw = dfpwrite(uf, ubuf, p - ubuf, &off); + off += nw; + } + noerror(); + xrwunlock(&ulk, Wr); +} + +int +writedenied(int uid) +{ + int r; + + if(uwrite == nil) + return 0; + xrwlock(&ulk, Rd); + r = findmember(uwrite, uid) == 0; + xrwunlock(&ulk, Rd); + return r; +} + +int +allowed(int uid) +{ + Usr *u; + int r; + + xrwlock(&ulk, Rd); + u = finduid(uid); + r = 0; + if(u) + r = u->allow; + xrwunlock(&ulk, Rd); + return r; } /* @@ -265,7 +507,9 @@ if(count <= 0) /* shouldn't happen */ return 0; + quiescent(Yes); s = recvp(fs->consc); + quiescent(No); tot = 0; do{ nr = strlen(s); @@ -318,28 +562,21 @@ } static void -cusers(int argc, char *argv[]) +cusers(int, char *[]) { int i; - Usr *u; + Usr *usr; - 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"); + xrwlock(&ulk, Rd); + if(catcherror()){ + xrwunlock(&ulk, Rd); + error(nil); } + for(i = 0; i < uidgen; i++) + if((usr=finduid(i)) != nil) + consprint("%A\n", usr); + noerror(); + xrwunlock(&ulk, Rd); } static void @@ -356,9 +593,7 @@ consprint("usage: %s [-c]\n", argv[0]); return; } - fsstats(clr); - ninestats(clr); - ixstats(clr); + consprint("%s\n", updatestats(clr)); } static void @@ -414,6 +649,89 @@ dumpfids(); } +static void +crwerr(int, char *argv[]) +{ + if(*argv[0] == 'r'){ + swreaderr = atoi(argv[1]); + fprint(2, "%s: sw read err count = %d\n", argv0, swreaderr); + }else{ + swwriteerr = atoi(argv[1]); + fprint(2, "%s: sw write err count = %d\n", argv0, swwriteerr); + } +} + +static void +ccheck(int argc, char *argv[]) +{ + switch(argc){ + case 1: + fscheck(); + break; + case 2: + if(strcmp(argv[1], "-v") == 0){ + if(fscheck() > 0) + fsdump(1, 0); + }else + consprint("usage: %s [-v]\n", argv[0]); + break; + default: + consprint("usage: %s [-v]\n", argv[0]); + } +} + +static void +clru(int, char**) +{ + fslru(); +} + + +static void +creclaim(int, char**) +{ + fsreclaim(); +} + +static void +callow(int argc, char *argv[]) +{ + Usr *u, *usr; + int i; + + usr = nil; + switch(argc){ + case 1: + if(*argv[0] == 'd') + for(i = 0; i < nelem(uids); i++) + for(u = uids[i]; u != nil; u = u->inext) + u->allow = 0; + break; + case 2: + xrwlock(&ulk, Wr); + usr = finduname(argv[1], Dontmk); + if(usr == nil){ + xrwunlock(&ulk, Wr); + consprint("user not found\n"); + return; + } + usr->allow = (*argv[0] == 'a'); + xrwunlock(&ulk, Wr); + break; + default: + consprint("usage: %s [uid]\n", argv[0]); + return; + } + xrwlock(&ulk, Rd); + for(i = 0; i < nelem(uids); i++) + for(u = uids[i]; u != nil; u = u->inext) + if(u->allow) + consprint("user '%s' is allowed\n", u->name); + else if(u == usr) + consprint("user '%s' is not allowed\n", u->name); + xrwunlock(&ulk, Rd); +} + static void chelp(int, char**); static Cmd cmds[] = @@ -423,10 +741,17 @@ {"stats", cstats, 0, "stats [-c]"}, {"sync", csync, 1, "sync"}, {"halt", chalt, 1, "halt"}, - {"users", cusers, 0, "users [-r|-w]"}, + {"users", cusers, 1, "users"}, {"debug", cdebug, 2, "cdebug [+-]FLAGS | on | off"}, {"locks", clocks, 2, "locks [on|off|dump]"}, {"fids", cfids, 1, "fids"}, + {"rerr", crwerr, 2, "rerr n"}, + {"werr", crwerr, 2, "werr n"}, + {"check", ccheck, 0, "check"}, + {"lru", clru, 1, "lru"}, + {"reclaim", creclaim, 1, "reclaim"}, + {"allow", callow, 0, "allow [uid]"}, + {"disallow", callow, 0, "disallow [uid]"}, {"?", chelp, 1, "?"}, }; @@ -447,7 +772,6 @@ consprint("creepy> "); } - long conswrite(char *ubuf, long count) { @@ -491,10 +815,17 @@ for(i = 0; i < nelem(cmds); i++){ if(strcmp(args[0], cmds[i].name) != 0) continue; + quiescent(Yes); + if(catcherror()){ + quiescent(No); + error(nil); + } if(cmds[i].nargs != 0 && cmds[i].nargs != nargs) consprint("usage: %s\n", cmds[i].usage); else cmds[i].f(nargs, args); + noerror(); + quiescent(No); break; } if(i == nelem(cmds)) diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/conf.h --- a/sys/src/cmd/creepy/conf.h Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/conf.h Fri Mar 16 16:01:01 2012 +0100 @@ -26,8 +26,9 @@ Niptr = 4, /* # of indirect data pointers */ #endif - Mminfree = 50, /* low on mem blocks */ - Dminfree = 1000, /* low on disk blocks */ + Mmaxdirtypcent = 50, /* Max % of blocks dirty in mem */ + Mminfree = 50, /* # blocks when low on mem blocks */ + Dminfree = 1000, /* # blocks when low on disk blocks */ Dminattrsz = Dblksz/2, /* min size for attributes */ Nahead = 10 * Dblksz, /* # of bytes to read ahead */ @@ -48,6 +49,12 @@ Mmaxfree = 2*Mminfree, /* high on mem blocks */ Dmaxfree = 2*Dminfree, /* high on disk blocks */ + Mzerofree = 10, /* out of memory blocks */ Dzerofree = 10, /* out of disk blocks */ + + Unamesz = 20, + Statsbufsz = 1024, + + Syncival = 60 * 1000, /* desired sync intervals (ms) */ }; diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/dblk.c --- a/sys/src/cmd/creepy/dblk.c Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/dblk.c Fri Mar 16 16:01:01 2012 +0100 @@ -20,37 +20,40 @@ int swreaderr, swwriteerr; void -checktag(u64int tag, uint type, u64int addr) +checktag(u64int tag, uint type, daddrt addr) { if(tag != TAG(type, addr)){ - fprint(2, "%s: bad tag: %#ullx != %#ux d%#ullx pc = %#p\n", + fprint(2, "%s: bad tag: %#ullx != %#ux d%#010ullx pc = %#p\n", argv0, tag, type, addr, getcallerpc(&tag)); -abort(); error("bad tag"); } } -void -okaddr(u64int addr) +static void +okaddr(daddrt addr) { - if((addr&Fakeaddr) == 0 && (addr < Dblksz || addr >= fs->limit)) + if((addr&Fakeaddr) == 0 && (addr < Dblksz || addr >= fs->limit)){ + fprint(2, "%s: okaddr: bad address d%#010ullx pc = %#p\n", + argv0, addr, getcallerpc(&addr)); + fatal("okaddr %#ullx", addr); error("okaddr %#ullx", addr); + } } -void -okdiskaddr(u64int addr) +static void +okdiskaddr(daddrt addr) { if((addr&Fakeaddr) != 0 || addr < Dblksz || addr >= fs->limit) fatal("okdiskaddr %#ullx", addr); } -void -dbclear(u64int tag, int type, u64int addr) +static void +dbclear(u64int tag, int type, daddrt addr) { static Diskblk d; static QLock lk; - dprint("dbclear type %s d%#ullx\n", tname(type), addr); + dWprint("dbclear type %s d%#ullx\n", tname(type), addr); xqlock(&lk); d.tag = tag; if(pwrite(fs->fd, &d, sizeof d, addr) != Dblksz){ @@ -67,7 +70,7 @@ if(canqlock(&fs->refs)) fatal("meltedref rlk"); if(rb->frozen){ - dprint("melted ref dirty=%d\n", rb->dirty); + dWprint("melted ref dirty=%d\n", rb->dirty); dbwrite(rb); rb->frozen = 0; } @@ -87,10 +90,10 @@ * issuing a warning so the user knows. */ -u64int +static daddrt newblkaddr(void) { - u64int addr, naddr; + daddrt addr, naddr; xqlock(fs); if(catcherror()){ @@ -155,23 +158,21 @@ return addr; } -u64int -addrofref(u64int refaddr, int idx) +daddrt +addrofref(daddrt refaddr, int idx) { return refaddr + idx*Dblksz; } -u64int -refaddr(u64int addr, int *idx) +static daddrt +refaddr(daddrt addr, int *idx) { - u64int bno, refaddr; + daddrt bno, refaddr; addr -= Dblk0addr; bno = addr/Dblksz; *idx = bno%Nblkgrpsz; refaddr = Dblk0addr + bno/Nblkgrpsz * Nblkgrpsz * Dblksz; - if(0)dprint("refaddr d%#ullx = d%#ullx[%d]\n", - Dblk0addr + addr, refaddr, *idx); return refaddr; } @@ -179,11 +180,11 @@ * db*ref() functions update the on-disk reference counters. * memory blocks use Memblk.Ref instead. Beware. */ -u64int -dbaddref(u64int addr, int delta, int set, Memblk **rbp, int *ip) +static daddrt +dbaddref(daddrt addr, int delta, int set, Memblk **rbp, int *ip) { Memblk *rb; - u64int raddr, ref; + daddrt raddr, ref; int i; if(addr == 0) @@ -225,24 +226,36 @@ return ref; } -u64int -dbgetref(u64int addr) +daddrt +dbgetref(daddrt addr) { return dbaddref(addr, 0, 0, nil, nil); } void -dbsetref(u64int addr, int ref) +dbsetref(daddrt addr, int ref) { dbaddref(addr, 0, ref, nil, nil); } -u64int -dbincref(u64int addr) +static daddrt +dbincref(daddrt addr) { return dbaddref(addr, +1, 0, nil, nil); } +static void +nodoublefree(daddrt addr) +{ + daddrt a; + + if(addr == 0) + return; + for(a = fs->super->d.free; a != 0; a = dbgetref(a)) + if(a == addr) + fatal("double free for addr d%#ullx", addr); +} + /* * Drop a on-disk reference. * When no references are left, the block is unlinked from the hash @@ -253,10 +266,10 @@ * just to release a reference to it * b may be nil if type and addr are given. */ -u64int -dbput(Memblk *b, int type, u64int addr) +daddrt +dbput(Memblk *b, int type, daddrt addr) { - u64int ref; + daddrt ref; Memblk *mb, *rb; int i, idx; @@ -265,7 +278,8 @@ okdiskaddr(addr); ref = dbgetref(addr); - dKprint("dbput d%#010ullx dr %#ullx type %s\n", addr, ref, tname(type)); + dKprint("dbput d%#010ullx dr %#ullx type %s pc=%#p\n", + addr, ref, tname(type), getcallerpc(&b)); if(ref > 2*Dblksz) fatal("dbput: d%#010ullx: double free", addr); @@ -286,11 +300,11 @@ if(type != DBdata) mb = dbget(type, addr); else - mb = mbget(type, addr, 0); + mb = mbget(type, addr, Dontmk); } if(mb != nil) assert(type == mb->type && addr == mb->addr); - dAprint("dbput: ref = 0 %H\n", mb); + dKprint("dbput: ref = 0 %H\n", mb); if(mb != nil) mbunhash(mb, 0); @@ -308,20 +322,33 @@ break; case DBfile: if(0)dbput(nil, DBattr, mb->d.aptr); - for(i = 0; i < nelem(mb->d.dptr); i++) + for(i = 0; i < nelem(mb->d.dptr); i++){ dbput(nil, DBdata, mb->d.dptr[i]); - for(i = 0; i < nelem(mb->d.iptr); i++) + mb->d.dptr[i] = 0; + } + for(i = 0; i < nelem(mb->d.iptr); i++){ dbput(nil, DBptr0+i, mb->d.iptr[i]); + mb->d.iptr[i] = 0; + } break; default: if(type < DBptr0 || type >= DBptr0+Niptr) fatal("dbput: type %d", type); - for(i = 0; i < Dptrperblk; i++) + for(i = 0; i < Dptrperblk; i++){ dbput(nil, mb->type-1, mb->d.ptr[i]); + mb->d.ptr[i] = 0; + } } noerror(); + if(mb != b) mbput(mb); + + if(dbg['d']) + assert(mbget(type, addr, Dontmk) == nil); + + if(dbg['d']) + nodoublefree(addr); xqlock(fs); xqlock(&fs->refs); rb->d.ref[idx] = fs->super->d.free; @@ -335,11 +362,11 @@ return ref; } -static u64int +static daddrt newfakeaddr(void) { - static u64int addr = ~0; - u64int n; + static daddrt addr = ~0; + daddrt n; xqlock(fs); addr -= Dblksz; @@ -352,7 +379,7 @@ dballoc(uint type) { Memblk *b; - u64int addr; + daddrt addr; int ctl; ctl = type == DBctl; @@ -386,7 +413,7 @@ * We know the format of all blocks and the type of all file * attributes. Those are the integers to convert to fix the bug. */ -Memblk* +static Memblk* hosttodisk(Memblk *b) { if(!TAGADDROK(b->d.tag, b->addr)) @@ -395,7 +422,7 @@ return b; } -void +static void disktohost(Memblk *b) { static union @@ -420,7 +447,7 @@ { Memblk *nb; static int nw; - u64int addr; + daddrt addr; if(b->addr&Fakeaddr) fatal("dbwrite: fake addr %H", b); @@ -435,11 +462,13 @@ dWprint("dbwrite at d%#010ullx %H\n",addr, b); nb = hosttodisk(b); if(swwriteerr != 0 && ++nw % swwriteerr == 0){ + written(b); /* what can we do? */ + mbput(nb); fprint(2, "%s: dbwrite: software fault injected\n", argv0); - mbput(nb); error("dbwrite: sw fault"); } if(pwrite(fs->fd, &nb->d, sizeof nb->d, addr) != Dblksz){ + written(b); /* what can we do? */ mbput(nb); fprint(2, "%s: dbwrite: d%#ullx: %r\n", argv0, b->addr); error("dbwrite: %r"); @@ -456,7 +485,7 @@ static int nr; long tot, n; uchar *p; - u64int addr; + daddrt addr; if(b->addr&Fakeaddr) fatal("dbread: fake addr %H", b); @@ -488,13 +517,13 @@ } Memblk* -dbget(uint type, u64int addr) +dbget(uint type, daddrt addr) { Memblk *b; dMprint("dbget %s d%#ullx\n", tname(type), addr); okaddr(addr); - b = mbget(type, addr, 1); + b = mbget(type, addr, Mkit); if(b == nil) error("i/o error"); if(b->loading == 0) @@ -513,7 +542,7 @@ if(type == DBfile){ assert(b->mf == nil); b->mf = anew(&mfalloc); - gmeta(b->mf, b->d.embed, Embedsz); + gmeta(b, b->d.embed, Embedsz); } b->loading = 0; noerror(); @@ -525,13 +554,13 @@ dupdentries(void *p, int n) { int i; - Dentry *d; + daddrt *d; d = p; for(i = 0; i < n; i++) - if(d[i].file != 0){ - dprint("add ref on melt d%#ullx\n", d[i].file); - dbincref(d[i].file); + if(d[i] != 0){ + dAprint("add ref on dup d%#ullx\n", d[i]); + dbincref(d[i]); } } @@ -544,8 +573,7 @@ { Memblk *nb; int i; - Mfile *nm; - ulong doff; + ulong doff, sz; nb = dballoc(b->type); if(catcherror()){ @@ -566,6 +594,7 @@ isrwlocked(b, Rd); nb->d.asize = b->d.asize; nb->d.aptr = b->d.aptr; + nb->d.ndents = b->d.ndents; if(nb->d.aptr != 0) dbincref(b->d.aptr); for(i = 0; i < nelem(b->d.dptr); i++){ @@ -578,13 +607,16 @@ if(nb->d.iptr[i] != 0) dbincref(b->d.iptr[i]); } + nb->d.Dmeta = b->d.Dmeta; memmove(nb->d.embed, b->d.embed, Embedsz); - nm = nb->mf; - gmeta(nm, nb->d.embed, Embedsz); - if((nm->mode&DMDIR) == 0) + gmeta(nb, nb->d.embed, Embedsz); + if((nb->d.mode&DMDIR) == 0) break; doff = embedattrsz(nb); - dupdentries(nb->d.embed+doff, (Embedsz-doff)/sizeof(Dentry)); + sz = Embedsz-doff; + if(sz > b->d.length) + sz = b->d.length; + dupdentries(nb->d.embed+doff, sz/Daddrsz); /* * no race: caller takes care. */ @@ -599,7 +631,7 @@ for(i = 0; i < Dptrperblk; i++){ nb->d.ptr[i] = b->d.ptr[i]; if(nb->d.ptr[i] != 0) - dbincref(b->d.ptr[i]); + dbincref(nb->d.ptr[i]); } } changed(nb); diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/dk.h --- a/sys/src/cmd/creepy/dk.h Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/dk.h Fri Mar 16 16:01:01 2012 +0100 @@ -1,4 +1,3 @@ -typedef struct Fmeta Fmeta; typedef struct Ddatablk Ddatablk; typedef struct Dptrblk Dptrblk; typedef struct Drefblk Drefblk; @@ -9,7 +8,6 @@ typedef struct Diskblkhdr Diskblkhdr; typedef struct Memblk Memblk; typedef struct Fsys Fsys; -typedef struct Dentry Dentry; typedef struct Dmeta Dmeta; typedef struct Blksl Blksl; typedef struct Mfile Mfile; @@ -20,6 +18,9 @@ typedef struct Lstat Lstat; typedef struct List List; typedef struct Link Link; +typedef struct Usr Usr; +typedef struct Member Member; + /* * Conventions: @@ -60,8 +61,6 @@ * - 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. * - frozen blocks are quiescent. - * - the block epoch number for a on-disk block is the time when it - * was written (thus it's archived "/" has a newer epoch). * - mb*() functions do not raise errors. * * Locking: @@ -91,9 +90,18 @@ Rd=0, Wr, + Dontmk = 0, + Mkit, + Tqlock = 0, Trwlock, Tlock, + + No = 0, + Yes, + + /* mtime is ns in creepy, but s in 9p */ + NSPERSEC = 1000000000ULL, }; @@ -107,6 +115,11 @@ }; +enum +{ + DMUSERS = 0x01000000ULL, + DMBITS = DMDIR|DMAPPEND|DMEXCL|DMTMP|0777, +}; #define HOWMANY(x, y) (((x)+((y)-1))/(y)) #define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) @@ -142,12 +155,15 @@ /*...*/ DBctl = ~0, /* DBfile, never on disk. arg for dballoc */ - Dblkhdrsz = 2*BIT64SZ, - Nblkgrpsz = (Dblksz - Dblkhdrsz) / BIT64SZ, + Daddrsz = BIT64SZ, + Dblkhdrsz = BIT64SZ, + Nblkgrpsz = (Dblksz - Dblkhdrsz) / Daddrsz, Dblk0addr = 2*Dblksz, }; +typedef u64int daddrt; /* disk addreses and sizes */ + struct Ddatablk { uchar data[1]; /* raw memory */ @@ -155,27 +171,31 @@ struct Dptrblk { - u64int ptr[1]; /* array of block addresses */ + daddrt ptr[1]; /* array of block addresses */ }; struct Drefblk { - u64int ref[1]; /* disk RC or next block in free list */ + daddrt ref[1]; /* disk RC or next block in free list */ }; struct Dattrblk { - u64int next; /* next block used for attribute data */ + daddrt next; /* next block used for attribute data */ uchar attr[1]; /* raw attribute data */ }; -/* - * directory entry. contents of data blocks in directories. - * Each block stores only an integral number of Dentries, for simplicity. - */ -struct Dentry +struct Dmeta /* mandatory metadata */ { - u64int file; /* file address or 0 when unused */ + u64int id; /* ctime, actually */ + u64int mode; + u64int atime; + u64int mtime; + u64int length; + u64int uid; + u64int gid; + u64int muid; + /* name\0 */ }; /* @@ -190,35 +210,20 @@ * The pointer in iptr[n] is an n-indirect data pointer. * * Directories are also files, but their data is simply an array of - * Dentries. + * disk addresses for files. + * + * To ensure embed is a multiple of dir entries, we declare it here as [8] + * and not as [1]. */ struct Dfileblk { u64int asize; /* attribute size */ - u64int aptr; /* attribute block pointer */ - u64int dptr[Ndptr]; /* direct data pointers */ - u64int iptr[Niptr]; /* indirect data pointers */ - uchar embed[1]; /* embedded attrs and data */ -}; - -enum -{ - FMuid = 0, /* strings in mandatory attributes */ - FMgid, - FMmuid, - FMname, - FMnstr, -}; - -struct Dmeta /* mandatory metadata */ -{ - u64int id; /* ctime, actually */ - u64int mode; - u64int atime; - u64int mtime; - u64int length; - u16int ssz[FMnstr]; - /* uid\0gid\0muid\0name\0 */ + u64int ndents; /* # of directory entries, for dirs */ + daddrt aptr; /* attribute block pointer */ + daddrt dptr[Ndptr]; /* direct data pointers */ + daddrt iptr[Niptr]; /* indirect data pointers */ + Dmeta; /* predefined attributes, followed by name */ + uchar embed[Daddrsz]; /* embedded attrs and data */ }; #define MAGIC 0x6699BCB06699BCB0ULL @@ -234,11 +239,12 @@ struct Dsuperblk { u64int magic; /* MAGIC */ - u64int free; /* first free block on list */ - u64int eaddr; /* end of the assigned disk portion */ - u64int root; /* address of /archive in disk */ + daddrt free; /* first free block on list */ + daddrt eaddr; /* end of the assigned disk portion */ + daddrt root; /* address of /archive in disk */ u64int oddrefs; /* use odd ref blocks? or even ref blocks? */ u64int ndfree; /* # of blocks in free list */ + u64int maxuid; /* 1st available uid */ u64int dblksz; /* only for checking */ u64int nblkgrpsz; /* only for checking */ u64int dminattrsz; /* only for checking */ @@ -247,8 +253,6 @@ u64int dblkdatasz; /* only for checking */ u64int embedsz; /* only for checking */ u64int dptrperblk; /* only for checking */ - uchar vac0[24]; /* score for last venti archive + 4pad */ - uchar vac1[24]; /* score for previous venti archive + 4pad */ }; enum @@ -294,13 +298,15 @@ /* * These are derived. + * Embedsz must compensate that embed[] was declared as embed[Daddrsz], + * to make it easy for the compiler to keep things aligned on 64 bits. */ enum { Dblkdatasz = sizeof(Diskblk) - sizeof(Diskblkhdr), - Embedsz = Dblkdatasz - sizeof(Dfileblk), - Dptrperblk = Dblkdatasz / sizeof(u64int), - Drefperblk = Dblkdatasz / sizeof(u64int), + Embedsz = Dblkdatasz - sizeof(Dfileblk) + Daddrsz, + Dptrperblk = Dblkdatasz / Daddrsz, + Drefperblk = Dblkdatasz / Daddrsz, }; @@ -315,31 +321,24 @@ */ /* - * File metadata - */ -struct Fmeta -{ - Dmeta; - char *uid; - char *gid; - char *muid; - char *name; -}; - -/* * On memory file information. */ struct Mfile { Mfile* next; /* in free list */ RWLock; - Fmeta; + + char *uid; /* reference to the user table */ + char *gid; /* reference to the user table */ + char *muid; /* reference to the user table */ + char *name; /* reference to the disk block */ Memblk* melted; /* next version for this one, if frozen */ ulong lastbno; /* last accessed block nb within this file */ ulong sequential; /* access has been sequential */ int open; /* for DMEXCL */ + int users; uvlong raoffset; /* we did read ahead up to this offset */ int wadone; /* we did walk ahead here */ }; @@ -364,7 +363,7 @@ struct Memblk { Ref; - u64int addr; /* block address */ + daddrt addr; /* block address */ Memblk *next; /* in hash or free list */ Link; /* lru / dirty / ref lists */ @@ -372,8 +371,10 @@ Mfile *mf; /* DBfile on-memory info. */ int type; + Lock dirtylk; int dirty; /* must be written */ int frozen; /* is frozen */ + int aflag; /* part of the "/archive" file? */ int loading; /* block is being read */ int changed; /* for freerefs/writerefs */ QLock newlk; /* only to wait on DBnew blocks */ @@ -409,7 +410,7 @@ List lru; /* hd: mru; tl: lru */ List mdirty; /* dirty blocks, not on lru */ - List refs; /* DBref blocks, not in lru nor dirty lists */ + List refs; /* DBref blocks, neither in lru nor dirty lists */ QLock mlk; Mfile *mfree; /* unused list */ @@ -420,23 +421,26 @@ Memblk *active; /* /active */ Memblk *archive; /* /archive */ Memblk *cons; /* /cons */ + Memblk *stats; /* /stats */ Channel *consc; /* of char*; output for /cons */ Memblk *fzsuper; /* frozen super */ - QLock fzlk; /* free or reclaim in progress. */ - char *dev; /* name for disk */ int fd; /* of disk */ - u64int limit; /* address for end of disk */ + daddrt limit; /* address for end of disk */ usize ndblk; /* # of disk blocks in dev */ - int config; /* config mode enabled */ - int nindirs[Niptr]; /* stats */ int nmelts; + QLock fzlk; /* freeze, melt, check, write */ + RWLock quiescence; /* any activity rlocks() this */ + QLock policy; /* fspolicy */ + uchar *chk; /* for fscheck() */ + + uvlong wtime; /* time for last fswrite */ }; /* @@ -481,8 +485,29 @@ int naf; }; +struct Member +{ + Member *next; + Usr *u; +}; + +struct Usr +{ + Usr *nnext; /* next by name */ + Usr *inext; /* next by id */ + + int id; + int enabled; + int allow; + Usr *lead; + char name[Unamesz]; + Member *members; +}; + #pragma varargck type "H" Memblk* +#pragma varargck type "A" Usr* +#pragma varargck type "P" Path* /* used in debug prints to print just part of huge values */ #define EP(e) ((e)&0xFFFFFFFFUL) @@ -492,5 +517,6 @@ extern Fsys*fs; extern uvlong maxfsz; -extern char*defaultusers; extern Alloc mfalloc, pathalloc; +extern int swreaderr, swwriteerr; +extern int fatalaborts; diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/fblk.c --- a/sys/src/cmd/creepy/fblk.c Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/fblk.c Fri Mar 16 16:01:01 2012 +0100 @@ -13,8 +13,7 @@ #include "fns.h" /* - * File block tools. - * Should be used mostly by file.c, where the interface is kept. + * File blocks. * see dk.h */ @@ -46,33 +45,27 @@ fatal("is%clocked at pc %#p", iswr?'w':'r', getcallerpc(&f)); } -void +static void isdir(Memblk *f) { if(f->type != DBfile || f->mf == nil) fatal("isdir: not a file at pc %#p", getcallerpc(&f)); - if((f->mf->mode&DMDIR) == 0) + if((f->d.mode&DMDIR) == 0) fatal("isdir: not a dir at pc %#p", getcallerpc(&f)); } -void -isnotdir(Memblk *f) -{ - if(f->type != DBfile || f->mf == nil) - fatal("isnotdir: not a file at pc %#p", getcallerpc(&f)); - if((f->mf->mode&DMDIR) != 0) - fatal("isnotdir: dir at pc %#p", getcallerpc(&f)); -} - /* for dfblk only */ static Memblk* -getmelted(uint isdir, int isarch, uint type, u64int *addrp) +getmelted(uint isdir, int isarch, uint type, daddrt *addrp, int *chg) { Memblk *b, *nb; + *chg = 0; if(*addrp == 0){ b = dballoc(type); *addrp = b->addr; + *chg = 1; + b->aflag = isarch; return b; } @@ -88,13 +81,14 @@ error(nil); } nb = dbdup(b); + assert(type == b->type); if(isdir && type == DBdata) - dupdentries(nb->d.data, Dblkdatasz/sizeof(Dentry)); + dupdentries(nb->d.data, Dblkdatasz/Daddrsz); USED(&nb); /* for error() */ *addrp = nb->addr; + *chg = 1; dbput(b, b->type, b->addr); noerror(); - incref(nb); mbput(b); return nb; } @@ -108,21 +102,24 @@ * * Read-ahead is not considered here. The file only records * the last accessed block number, to help the caller do RA. + * + * Blocks added to "/archive" are flagged with aflag, as an aid + * to write them even though "/archive" is never frozen. */ static Memblk* dfblk(Memblk *f, ulong bno, int mkit) { ulong prev, nblks; - int i, idx, nindir, type, isdir, isarch; + int i, idx, nindir, type, isdir, isarch, chg; Memblk *b, *pb; - u64int *addrp; + daddrt *addrp; isarch = f == fs->archive; if(isarch) f->frozen = 0; if(mkit) ismelted(f); - isdir = (f->mf->mode&DMDIR); + isdir = (f->d.mode&DMDIR); if(bno != f->mf->lastbno){ f->mf->sequential = (!mkit && bno == f->mf->lastbno + 1); @@ -134,16 +131,21 @@ * prev: # of blocks before the current one. */ prev = 0; + chg = 0; /* * Direct block? */ - if(bno < nelem(f->d.dptr)) + if(bno < nelem(f->d.dptr)){ if(mkit) - return getmelted(isdir, isarch, DBdata, &f->d.dptr[bno]); + b = getmelted(isdir, isarch, DBdata, &f->d.dptr[bno], &chg); else - return dbget(DBdata, f->d.dptr[bno]); - + b = dbget(DBdata, f->d.dptr[bno]); + if(chg) + changed(f); + b->aflag = isarch; + return b; + } bno -= nelem(f->d.dptr); prev += nelem(f->d.dptr); @@ -168,9 +170,12 @@ addrp = &f->d.iptr[i]; if(mkit) - b = getmelted(isdir, isarch, type, addrp); + b = getmelted(isdir, isarch, type, addrp, &chg); else b = dbget(type, *addrp); + b->aflag = isarch; + if(chg) + changed(f); pb = b; if(catcherror()){ mbput(pb); @@ -184,6 +189,7 @@ * nblks: # of data blocks addressed by b */ for(nindir = i+1; nindir >= 0; nindir--){ + chg = 0; dFprint("indir %s d%#ullx nblks %uld ptrperblk %d bno %uld\n\n", tname(DBdata+nindir), *addrp, nblks, Dptrperblk, bno); idx = 0; @@ -193,13 +199,18 @@ } if(*addrp == 0 && !mkit){ /* hole */ + fprint(2, "HOLE\n"); b = nil; }else{ assert(type >= DBdata); if(mkit) - b = getmelted(isdir, isarch, type, addrp); + b = getmelted(isdir, isarch, type, addrp, &chg); else b = dbget(type, *addrp); + b->aflag = isarch; + + if(chg) + changed(pb); addrp = &b->d.ptr[idx]; } mbput(pb); @@ -211,6 +222,7 @@ type--; } noerror(); + return b; } @@ -225,7 +237,6 @@ isrwlocked(f, Wr); ismelted(f); - isnotdir(f); dprint("dfdropblks: could remove d%#ullx[%uld:%uld]\n", f->addr, bno, bend); @@ -244,7 +255,7 @@ for(; bno < bend; bno++){ if(catcherror()) continue; - b = dfblk(f, bno, 0); + b = dfblk(f, bno, Dontmk); noerror(); memset(b->d.data, 0, Dblkdatasz); changed(b); @@ -275,24 +286,13 @@ return off/Dblkdatasz; } -static void -updatesize(Memblk *f, uvlong nsize) -{ - Dmeta *d; - - isrwlocked(f, Wr); - f->mf->length = nsize; - d = (Dmeta*)f->d.embed; - d->length = nsize; -} - /* * Return a block slice for data in f. * The slice returned is resized to keep in a single block. * If there's a hole in the file, Blksl.data == nil && Blksl.len > 0. * * If mkit, the data block (and any pointer block crossed) - * is allocated/melted if needed, and the file length updated. + * is allocated/melted if needed, but the file length is NOT updated. * * The file must be r/wlocked by the caller, and melted if mkit. * The block is returned referenced but unlocked, @@ -309,7 +309,7 @@ if(iswr) ismelted(f); else - if(off >= f->mf->length) + if(off >= f->d.length) goto done; doff = embedattrsz(f); @@ -334,11 +334,10 @@ if(sl.len > len) sl.len = len; - if(off + sl.len > f->mf->length) - if(iswr) - updatesize(f, off + sl.len); - else - sl.len = f->mf->length - off; + if(off + sl.len > f->d.length) + if(!iswr) + sl.len = f->d.length - off; + /* else the file size will be updated by the caller */ done: if(sl.b == nil){ dFprint("slice m%#p[%#ullx:+%#ulx]%c -> 0[%#ulx]\n", @@ -358,31 +357,6 @@ return sl; } -static void -compact(Memblk *d, Dentry *de, u64int off) -{ - Blksl sl; - uvlong lastoff; - Dentry *lastde; - - if(catcherror()) - return; - assert(d->mf->length >= sizeof(Dentry)); - lastoff = d->mf->length - sizeof(Dentry); - if(d->mf->length > sizeof(Dentry) && off < lastoff){ - sl = dfslice(d, sizeof(Dentry), lastoff, 0); - assert(sl.b); - lastde = sl.data; - de->file = lastde->file; - lastde->file = 0; - changed(sl.b); - mbput(sl.b); - } - noerror(); - updatesize(d, lastoff); - changed(d); /*paranoia: caller of dfchdentry calls dfchanged*/ -} - /* * Find a dir entry for addr (perhaps 0 == avail) and change it to * naddr. If iswr, the entry is allocated if needed and the blocks @@ -390,25 +364,33 @@ * 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) +uvlong +dfchdentry(Memblk *d, u64int addr, u64int naddr, int mkit) { Blksl sl; - Dentry *de; + daddrt *de; uvlong off; int i; + assert(d->d.length/Daddrsz >= d->d.ndents); + dAprint("dfchdentry d%#ullx -> d%#ullx\nin %H\n", addr, naddr, d); - isrwlocked(d, iswr); + isrwlocked(d, mkit?Wr:Rd); isdir(d); + if(addr == naddr) + fatal("dfchdentry: it did happen. now, why?"); + off = 0; + for(;;){ - sl = dfslice(d, Dblkdatasz, off, iswr); - if(sl.len == 0) + sl = dfslice(d, Dblkdatasz, off, mkit); + if(sl.len == 0){ + assert(sl.b == nil); break; + } if(sl.b == nil){ - if(addr == 0 && !iswr) + if(addr == 0 && !mkit) return off; continue; } @@ -417,34 +399,42 @@ error(nil); } de = sl.data; - for(i = 0; i < sl.len/sizeof(Dentry); i++){ - if(de[i].file == addr){ + for(i = 0; i < sl.len/Daddrsz; i++){ + if(de[i] == addr){ + off += i*Daddrsz; if(naddr != addr){ - if(iswr && naddr == 0) - compact(d, &de[i], off+i*sizeof(Dentry)); - else - de[i].file = naddr; + de[i] = naddr; changed(sl.b); + if(addr == 0 && naddr != 0){ + if(d->d.length < off+Daddrsz) + d->d.length = off+Daddrsz; + d->d.ndents++; + changed(d); + }else if(addr != 0 && naddr == 0){ + d->d.ndents--; + changed(d); + } } noerror(); mbput(sl.b); - return off + i*sizeof(Dentry); + assert(d->d.length/Daddrsz >= d->d.ndents); + return off; } } off += sl.len; noerror(); mbput(sl.b); } - if(iswr) + if(mkit) fatal("dfchdentry: bug"); return Noaddr; } -static u64int +static daddrt dfdirnth(Memblk *d, int n) { Blksl sl; - Dentry *de; + daddrt *de; uvlong off; int i, tot; @@ -452,18 +442,20 @@ off = 0; tot = 0; for(;;){ - sl = dfslice(d, Dblkdatasz, off, 0); - if(sl.len == 0) + sl = dfslice(d, Dblkdatasz, off, Rd); + if(sl.len == 0){ + assert(sl.b == nil); break; - if(sl.b == 0) + } + if(sl.b == nil) continue; de = sl.data; - for(i = 0; i < sl.len/sizeof(Dentry); i++) - if(de[i].file != 0 && tot++ >= n){ + for(i = 0; i < sl.len/Daddrsz; i++) + if(de[i] != 0 && tot++ >= n){ + dFprint("dfdirnth d%#ullx[%d] = d%#ullx\n", + d->addr, n, de[i]); mbput(sl.b); - dFprint("dfdirnth d%#ullx[%d] = d%#ullx\n", - d->addr, n, de[i].file); - return de[i].file; + return de[i]; } off += sl.len; mbput(sl.b); @@ -474,14 +466,14 @@ static Memblk* xfchild(Memblk *f, int n, int disktoo) { - u64int addr; + daddrt addr; Memblk *b; addr = dfdirnth(f, n); if(addr == 0) return nil; - b = mbget(DBfile, addr, 0); - if(b != nil || disktoo == 0) + b = mbget(DBfile, addr, Dontmk); + if(b != nil || disktoo == Mem) return b; return dbget(DBfile, addr); } @@ -489,13 +481,13 @@ Memblk* dfchild(Memblk *f, int n) { - return xfchild(f, n, 1); + return xfchild(f, n, Disk); } -Memblk* +static Memblk* mfchild(Memblk *f, int n) { - return xfchild(f, n, 0); + return xfchild(f, n, Mem); } /* @@ -508,20 +500,20 @@ ismelted(d); isdir(d); - dfchdentry(d, 0, f->addr, Wr); + dfchdentry(d, 0, f->addr, Mkit); } /* * does not dbput(f) * caller locks both d and f */ -void +static void dfunlink(Memblk *d, Memblk *f) { ismelted(d); isdir(d); - dfchdentry(d, f->addr, 0, Wr); + dfchdentry(d, f->addr, 0, Mkit); } /* @@ -533,10 +525,10 @@ { Memblk *f; Blksl sl; - Dentry *de; + daddrt *de; uvlong off; int i; - + if(strcmp(name, "..") == 0) fatal("dfwalk: '..'"); isdir(d); @@ -546,9 +538,11 @@ off = 0; f = nil; for(;;){ - sl = dfslice(d, Dblkdatasz, off, 0); - if(sl.len == 0) + sl = dfslice(d, Dblkdatasz, off, Rd); + if(sl.len == 0){ + assert(sl.b == nil); break; + } if(sl.b == nil) continue; if(catcherror()){ @@ -556,12 +550,12 @@ mbput(sl.b); error(nil); } - for(i = 0; i < sl.len/sizeof(Dentry); i++){ + for(i = 0; i < sl.len/Daddrsz; i++){ de = sl.data; de += i; - if(de->file == 0) + if(*de == 0) continue; - f = dbget(DBfile, de->file); + f = dbget(DBfile, *de); if(strcmp(f->mf->name, name) != 0){ mbput(f); continue; @@ -589,26 +583,54 @@ * Return the last version for *fp, wlocked, be it frozen or melted. */ static void -followmelted(Memblk **fp) +followmelted(Memblk **fp, int iswr) { Memblk *f; f = *fp; isfile(f); - rwlock(f, Wr); + rwlock(f, iswr); while(f->mf->melted != nil){ incref(f->mf->melted); *fp = f->mf->melted; - rwunlock(f, Wr); + rwunlock(f, iswr); mbput(f); f = *fp; - rwlock(f, Wr); + rwlock(f, iswr); if(!f->frozen) return; } } /* + * Advance path to use the most recent version of each file. + */ +Path* +dflast(Path **pp, int nth) +{ + Memblk *f; + Path *p; + int i; + + p = *pp; + for(i = 0; i < nth; i++){ + f = p->f[i]; + if(f != nil && f->mf != nil && f->mf->melted != nil) + break; + } + if(i == nth) + return p; /* all files have the last version */ + + ownpath(pp); + p = *pp; + for(i = 0; i < nth; i++){ + followmelted(&p->f[i], Rd); + rwunlock(p->f[i], Rd); + } + return p; +} + +/* * Caller walked down p, and now requires the nth element to be * melted, and wlocked for writing. (nth count starts at 1); * @@ -632,7 +654,7 @@ /* * 1. Optimistic: Try to get a loaded melted version for f. */ - followmelted(fp); + followmelted(fp, Wr); f = *fp; if(!f->frozen) return p; @@ -650,7 +672,7 @@ * p[1] is /active */ for(;;){ - followmelted(&p->f[1]); + followmelted(&p->f[1], Wr); if(p->f[1]->frozen == 0) break; rwunlock(p->f[1], Wr); @@ -661,7 +683,7 @@ * At the end of the loop, p->f[i] is melted and wlocked. */ for(i = 2; i < nth; i++){ - followmelted(&p->f[i]); + followmelted(&p->f[i], Wr); if(!p->f[i]->frozen){ rwunlock(p->f[i-1], Wr); continue; @@ -680,7 +702,7 @@ mbput(nf); error(nil); } - dfchdentry(p->f[i-1], p->f[i]->addr, nf->addr, 1); + dfchdentry(p->f[i-1], p->f[i]->addr, nf->addr, Mkit); noerror(); noerror(); /* committed */ @@ -698,27 +720,492 @@ return p; } +void +dfused(Path *p) +{ + Memblk *f; + + f = p->f[p->nf-1]; + isfile(f); + rwlock(f, Wr); + f->d.atime = nsec(); + rwunlock(f, Wr); +} + /* * Report that a file has been modified. * Modification times propagate up to the root of the file tree. + * But frozen files are never changed. */ void -dfchanged(Path *p) +dfchanged(Path *p, int muid) { Memblk *f; - u64int t; + u64int t, u; int i; - t = now(); + t = nsec(); + u = muid; for(i = 0; i < p->nf; i++){ f = p->f[i]; rwlock(f, Wr); if(f->frozen == 0) if(!catcherror()){ - wmtime(f, &t, sizeof t); - watime(f, &t, sizeof t); + f->d.mtime = t; + f->d.atime = t; + f->d.muid = muid; + changed(f); noerror(); } rwunlock(f, Wr); } } + +/* + * May be called with null parent, for root and ctl files. + * The first call with a null parent is root, all others are ctl + * files linked at root. + */ +Memblk* +dfcreate(Memblk *parent, char *name, int uid, ulong mode) +{ + Memblk *nf; + Mfile *m; + int isctl; + + if(fsfull()) + error("file system full"); + isctl = parent == nil; + if(parent == nil) + parent = fs->root; + + if(parent != nil){ + dprint("dfcreate '%s' %M at\n%H\n", name, mode, parent); + isdir(parent); + isrwlocked(parent, Wr); + ismelted(parent); + }else + dprint("dfcreate '%s' %M", name, mode); + + if(isctl) + nf = dballoc(DBctl); + else + nf = dballoc(DBfile); + if(catcherror()){ + mbput(nf); + if(parent != nil) + rwunlock(parent, Wr); + error(nil); + } + + m = nf->mf; + nf->d.id = nsec(); + nf->d.mode = mode; + nf->d.mtime = nf->d.id; + nf->d.atime = nf->d.id; + nf->d.length = 0; + m->uid = usrname(uid); + nf->d.uid = uid; + m->gid = m->uid; + nf->d.gid = nf->d.uid; + m->muid = m->uid; + nf->d.muid = nf->d.uid; + m->name = name; + nf->d.asize = pmeta(nf->d.embed, Embedsz, nf); + changed(nf); + + if(parent != nil){ + m->gid = parent->mf->gid; + nf->d.gid = parent->d.gid; + dflink(parent, nf); + } + noerror(); + dprint("dfcreate-> %H\n within %H\n", nf, parent); + return nf; +} + +void +dfremove(Memblk *p, Memblk *f) +{ + vlong n; + + /* funny as it seems, we may need extra blocks to melt */ + if(fsfull()) + error("file system full"); + + isrwlocked(f, Wr); + isrwlocked(p, Wr); + ismelted(p); + if((f->d.mode&DMDIR) != 0 && f->d.ndents > 0) + error("directory not empty"); + incref(p); + if(catcherror()){ + mbput(p); + error(nil); + } + dfunlink(p, f); + /* shouldn't fail now. it's unlinked */ + + if(p->d.ndents == 0 && p->d.length > 0){ /* all gone, make it public */ + p->d.length = 0; + changed(p); + } + + noerror(); + rwunlock(f, Wr); + if(!catcherror()){ + n = dfreclaim(f); + dprint("dfreclaim d%#ullx: %lld blks\n", f->addr, n); + noerror(); + } + mbput(f); + mbput(p); +} + +/* + * It's ok if a is nil, for reading ahead. + */ +ulong +dfpread(Memblk *f, void *a, ulong count, uvlong off) +{ + Blksl sl; + ulong tot; + char *p; + + p = a; + isrwlocked(f, Rd); + for(tot = 0; tot < count; tot += sl.len){ + sl = dfslice(f, count-tot, off+tot, Rd); + if(sl.len == 0){ + assert(sl.b == nil); + break; + } + if(sl.data == nil){ + memset(p+tot, 0, sl.len); + assert(sl.b == nil); + continue; + } + if(p != nil) + memmove(p+tot, sl.data, sl.len); + mbput(sl.b); + } + return tot; +} + +ulong +dfpwrite(Memblk *f, void *a, ulong count, uvlong *off) +{ + Blksl sl; + ulong tot; + char *p; + + if(fsfull()) + error("file system full"); + + isrwlocked(f, Wr); + ismelted(f); + p = a; + if(f->d.mode&DMAPPEND) + *off = f->d.length; + for(tot = 0; tot < count;){ + 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); + changed(sl.b); + mbput(sl.b); + tot += sl.len; + if(*off+tot > f->d.length){ + f->d.length = *off+tot; + changed(f); + } + } + return tot; +} + +static int +ptrmap(daddrt addr, int nind, Blkf f, int isdisk) +{ + int i; + Memblk *b; + long tot; + + if(addr == 0) + return 0; + if(isdisk) + b = dbget(DBdata+nind, addr); + else{ + b = mbget(DBdata+nind, addr, Dontmk); + if(b == nil) + return 0; /* on disk */ + } + if(catcherror()){ + mbput(b); + error(nil); + } + tot = 0; + if(f == nil || f(b) == 0){ + tot++; + if(nind > 0){ + for(i = 0; i < Dptrperblk; i++) + tot += ptrmap(b->d.ptr[i], nind-1, f, isdisk); + } + } + noerror(); + mbput(b); + return tot; +} + +/* + * CAUTION: debug: no locks. + */ +int +dfdump(Memblk *f, int isdisk) +{ + int i; + Memblk *b; + Memblk *(*child)(Memblk*, int); + long tot; + extern int mbtab; + + isfile(f); + tot = 1; + /* visit the blocks to fetch them if needed. */ + for(i = 0; i < nelem(f->d.dptr); i++) + tot += ptrmap(f->d.dptr[i], 0, nil, isdisk); + for(i = 0; i < nelem(f->d.iptr); i++) + tot += ptrmap(f->d.iptr[i], i+1, nil, isdisk); + fprint(2, "%H\n", f); + if((f->d.mode&DMDIR) != 0){ + mbtab++; + child = dfchild; + if(!isdisk) + child = mfchild; + for(i = 0; i < f->d.length/Daddrsz; i++){ + b = child(f, i); + if(b == nil) + continue; + if(!catcherror()){ + tot += dfdump(b, isdisk); + noerror(); + } + mbput(b); + } + mbtab--; + } + + return tot; +} + +static int +bfreeze(Memblk *b) +{ + if(b->frozen) + return -1; + b->frozen = 1; + return 0; +} + +int +dffreeze(Memblk *f) +{ + int i; + Memblk *b; + long tot; + + isfile(f); + if(f->frozen && f != fs->active && f != fs->archive) + return 0; + rwlock(f, Wr); + if(catcherror()){ + rwunlock(f, Wr); + error(nil); + } + f->frozen = 1; + tot = 1; + for(i = 0; i < nelem(f->d.dptr); i++) + tot += ptrmap(f->d.dptr[i], 0, bfreeze, Mem); + for(i = 0; i < nelem(f->d.iptr); i++) + tot += ptrmap(f->d.iptr[i], i+1, bfreeze, Mem); + if((f->d.mode&DMDIR) != 0){ + for(i = 0; i < f->d.length/Daddrsz; i++){ + b = mfchild(f, i); + if(b == nil) + continue; + if(!catcherror()){ + tot += dffreeze(b); + noerror(); + } + mbput(b); + } + } + noerror(); + rwunlock(f, Wr); + return tot; +} + +static int +countref(daddrt addr) +{ + ulong idx; + int old; + + idx = addr/Dblksz; + old = fs->chk[idx]; + if(fs->chk[idx] == 0xFE) + fprint(2, "fscheck: d%#010ullx: too many refs, ignoring some\n", + addr); + else + fs->chk[idx]++; + return old; +} + +static int +bcountrefs(Memblk *b) +{ + if(countref(b->addr) != 0) + return -1; + return 0; +} + +static void +countfree(daddrt addr) +{ + long i; + + i = addr/Dblksz; + if(fs->chk[i] != 0 && fs->chk[i] <= 0xFE) + fprint(2, "fscheck: d%#010ullx: free block in use\n", addr); + else if(fs->chk[i] == 0xFF) + fprint(2, "fscheck: d%#010ullx: double free\n", addr); + else + fs->chk[i] = 0xFF; +} + +void +dfcountfree(void) +{ + daddrt addr; + + dprint("list...\n"); + addr = fs->super->d.free; + while(addr != 0){ + if(addr >fs->limit){ + fprint(2, "fscheck: d%#010ullx: free overflow\n", addr); + break; + } + countfree(addr); + addr = dbgetref(addr); + } + /* heading unused part */ + dprint("hdr...\n"); + for(addr = 0; addr < Dblk0addr; addr += Dblksz) + countfree(addr); + /* DBref blocks */ + dprint("refs...\n"); + for(addr = Dblk0addr; addr < fs->super->d.eaddr; addr += Dblksz*Nblkgrpsz){ + countfree(addr); /* even DBref */ + countfree(addr+Dblksz); /* odd DBref */ + } +} + +long +dfcountrefs(Memblk *f) +{ + Memblk *b; + int i; + long nfails; + + nfails = 0; + isfile(f); + if((f->addr&Fakeaddr) == 0 && f->addr >= fs->limit){ + fprint(2, "fscheck: '%s' d%#010ullx: out of range\n", + f->mf->name, f->addr); + return 1; + } + if((f->addr&Fakeaddr) == 0) + if(countref(f->addr) != 0) /* already visited */ + return 0; /* skip children */ + rwlock(f, Rd); + if(catcherror()){ + fprint(2, "fscheck: '%s' d%#010ullx: data: %r\n", + f->mf->name, f->addr); + rwunlock(f, Rd); + return 1; + } + for(i = 0; i < nelem(f->d.dptr); i++) + ptrmap(f->d.dptr[i], 0, bcountrefs, Disk); + for(i = 0; i < nelem(f->d.iptr); i++) + ptrmap(f->d.iptr[i], i+1, bcountrefs, Disk); + if(f->d.mode&DMDIR) + for(i = 0; i < f->d.length/Daddrsz; i++){ + b = dfchild(f, i); + if(b == nil) + continue; + if(catcherror()){ + fprint(2, "fscheck: '%s' d%#010ullx:" + " child[%d]: %r\n", + f->mf->name, f->addr, i); + nfails++; + }else{ + nfails += dfcountrefs(b); + noerror(); + } + mbput(b); + } + noerror(); + rwunlock(f, Rd); + return nfails; +} + +/* + * Drop one disk reference for f and reclaim its storage if it's gone. + * The given memory reference is not released. + * For directories, all files contained have their disk references adjusted, + * and they are also reclaimed if no further references exist. + * + * NB: Time ago, directories were not in compact form (they had holes + * due to removals) and this had a bug while reclaiming that could lead + * to double frees of disk blocks. + * The bug was fixed, but since then, directories have changed again to + * have holes. If the a double free happens again, this is the place where + * to look, besides dbdup and dfchdentry. + */ +int +dfreclaim(Memblk *f) +{ + int i; + Memblk *b; + long tot; + + isfile(f); + dKprint("dfreclaim %H\n", f); + /* + * Remove children if it's the last disk ref before we drop data blocks. + * No new disk refs may be added, so there's no race here. + */ + tot = 0; + if(dbgetref(f->addr) == 1 && (f->d.mode&DMDIR) != 0){ + rwlock(f, Wr); + if(catcherror()){ + rwunlock(f, Wr); + error(nil); + } + for(i = 0; i < f->d.length/Daddrsz; i++){ + b = dfchild(f, i); + if(b == nil) + continue; + if(!catcherror()){ + tot += dfreclaim(b); + noerror(); + } + mbput(b); + } + noerror(); + rwunlock(f, Wr); + } + + if(dbput(f, f->type, f->addr) == 0) + tot++; + return tot; +} diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/file.c --- a/sys/src/cmd/creepy/file.c Fri Mar 16 15:57:01 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,450 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "conf.h" -#include "dbg.h" -#include "dk.h" -#include "ix.h" -#include "net.h" -#include "fns.h" - -/* - * Interface to handle files. - * see dk.h - */ - -static void -dfused(Memblk *f) -{ - u64int t; - - isfile(f); - t = now(); - wmtime(f, &t, sizeof t); -} - -/* - * May be called with null parent, for root and ctl files. - * The first call with a null parent is root, all others are ctl - * files linked at root. - */ -Memblk* -dfcreate(Memblk *parent, char *name, char *uid, ulong mode) -{ - Memblk *nf; - Mfile *m; - int isctl; - - if(fsfull()) - error("file system full"); - isctl = parent == nil; - if(parent == nil) - parent = fs->root; - - if(parent != nil){ - dprint("dfcreate '%s' %M at\n%H\n", name, mode, parent); - isdir(parent); - isrwlocked(parent, Wr); - ismelted(parent); - }else - dprint("dfcreate '%s' %M", name, mode); - - if(isctl) - nf = dballoc(DBctl); - else - nf = dballoc(DBfile); - if(catcherror()){ - mbput(nf); - if(parent != nil) - rwunlock(parent, Wr); - error(nil); - } - - m = nf->mf; - m->id = now(); - m->mode = mode; - m->mtime = m->id; - m->atime = m->id; - m->length = 0; - m->uid = uid; - m->gid = uid; - m->muid = uid; - m->name = name; - nf->d.asize = pmeta(nf->d.embed, Embedsz, m); - changed(nf); - - if(parent != nil){ - m->gid = parent->mf->uid; - dflink(parent, nf); - } - noerror(); - dprint("dfcreate-> %H\n", nf); - return nf; -} - -void -dfremove(Memblk *p, Memblk *f) -{ - vlong n; - - /* funny as it seems, we may need extra blocks to melt */ - if(fsfull()) - error("file system full"); - - isrwlocked(f, Wr); - isrwlocked(p, Wr); - ismelted(p); - if((f->mf->mode&DMDIR) != 0 && f->mf->length > 0) - error("directory not empty"); - incref(p); - if(catcherror()){ - mbput(p); - error(nil); - } - dfunlink(p, f); - /* shouldn't fail now. it's unlinked */ - noerror(); - rwunlock(f, Wr); - if(!catcherror()){ - n = dfreclaim(f); - dprint("dfreclaim d%#ullx: %lld blks\n", f->addr, n); - noerror(); - } - mbput(f); - mbput(p); -} - -ulong -dfpread(Memblk *f, void *a, ulong count, uvlong off) -{ - Blksl sl; - ulong tot; - char *p; - - p = a; - isrwlocked(f, Rd); - for(tot = 0; tot < count; tot += sl.len){ - sl = dfslice(f, count-tot, off+tot, Rd); - if(sl.len == 0) - break; - if(sl.data == nil){ - memset(p+tot, 0, sl.len); - continue; - } - memmove(p+tot, sl.data, sl.len); - mbput(sl.b); - } - dfused(f); - return tot; -} - -ulong -dfpwrite(Memblk *f, void *a, ulong count, uvlong *off) -{ - Blksl sl; - ulong tot; - char *p; - - if(fsfull()) - error("file system full"); - - 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); - if(sl.len == 0 || sl.data == nil) - fatal("dfpwrite: bug"); - memmove(sl.data, p+tot, sl.len); - changed(sl.b); - mbput(sl.b); - } - return tot; -} - -static int -ptrmap(u64int addr, int nind, Blkf f, int isdisk) -{ - int i; - Memblk *b; - long tot; - - if(addr == 0) - return 0; - if(isdisk) - b = dbget(DBdata+nind, addr); - else{ - b = mbget(DBdata+nind, addr, 0); - if(b == nil) - return 0; /* on disk */ - } - if(catcherror()){ - mbput(b); - error(nil); - } - tot = 0; - if(f == nil || f(b) == 0){ - tot++; - /* we might sweep an entire disk and run out of blocks */ - if(isdisk) - fslru(); - if(nind > 0){ - for(i = 0; i < Dptrperblk; i++) - tot += ptrmap(b->d.ptr[i], nind-1, f, isdisk); - } - } - noerror(); - mbput(b); - return tot; -} - -/* - * CAUTION: debug: no locks. - */ -int -dfdump(Memblk *f, int isdisk) -{ - int i; - Memblk *b; - Memblk *(*child)(Memblk*, int); - long tot; - extern int mbtab; - - isfile(f); - tot = 1; - /* visit the blocks to fetch them if needed. */ - for(i = 0; i < nelem(f->d.dptr); i++) - tot += ptrmap(f->d.dptr[i], 0, nil, isdisk); - for(i = 0; i < nelem(f->d.iptr); i++) - tot += ptrmap(f->d.iptr[i], i+1, nil, isdisk); - fprint(2, "%H\n", f); - if((f->mf->mode&DMDIR) != 0){ - mbtab++; - child = dfchild; - if(!isdisk) - child = mfchild; - for(i = 0; i < f->mf->length/sizeof(Dentry); i++){ - b = child(f, i); - if(b == nil) - continue; - if(!catcherror()){ - tot += dfdump(b, isdisk); - noerror(); - } - mbput(b); - } - mbtab--; - } - - /* we might sweep an entire disk and run out of blocks */ - if(isdisk) - fslru(); - return tot; -} - -static int -bfreeze(Memblk *b) -{ - if(b->frozen) - return -1; - b->frozen = 1; - return 0; -} - -int -dffreeze(Memblk *f) -{ - int i; - Memblk *b; - long tot; - - isfile(f); - if(f->frozen && f != fs->active && f != fs->archive) - return 0; - rwlock(f, Wr); - if(catcherror()){ - rwunlock(f, Wr); - error(nil); - } - f->frozen = 1; - tot = 1; - for(i = 0; i < nelem(f->d.dptr); i++) - tot += ptrmap(f->d.dptr[i], 0, bfreeze, Mem); - for(i = 0; i < nelem(f->d.iptr); i++) - tot += ptrmap(f->d.iptr[i], i+1, bfreeze, Mem); - if((f->mf->mode&DMDIR) != 0){ - for(i = 0; i < f->mf->length/sizeof(Dentry); i++){ - b = mfchild(f, i); - if(b == nil) - continue; - if(!catcherror()){ - tot += dffreeze(b); - noerror(); - } - mbput(b); - } - } - noerror(); - rwunlock(f, Wr); - return tot; -} - -static int -countref(u64int addr) -{ - ulong idx; - int old; - - idx = addr/Dblksz; - old = fs->chk[idx]; - if(fs->chk[idx] == 0xFE) - fprint(2, "fscheck: d%#010ullx: too many refs, ignoring some\n", - addr); - else - fs->chk[idx]++; - return old; -} - -static int -bcountrefs(Memblk *b) -{ - countref(b->addr); - return 0; -} - -static void -countfree(u64int addr) -{ - long i; - - i = addr/Dblksz; - if(fs->chk[i] != 0 && fs->chk[i] <= 0xFE) - fprint(2, "fscheck: d%#010ullx: free block in use\n", addr); - else if(fs->chk[i] == 0xFF) - fprint(2, "fscheck: d%#010ullx: double free\n", addr); - else - fs->chk[i] = 0xFF; -} - -void -dfcountfree(void) -{ - u64int addr; - - dprint("list...\n"); - addr = fs->super->d.free; - while(addr != 0){ - if(addr >fs->limit){ - fprint(2, "fscheck: d%#010ullx: free overflow\n", addr); - break; - } - countfree(addr); - addr = dbgetref(addr); - } - /* heading unused part */ - dprint("hdr...\n"); - for(addr = 0; addr < Dblk0addr; addr += Dblksz) - countfree(addr); - /* DBref blocks */ - dprint("refs...\n"); - for(addr = Dblk0addr; addr < fs->super->d.eaddr; addr += Dblksz*Nblkgrpsz){ - countfree(addr); /* even DBref */ - countfree(addr+Dblksz); /* odd DBref */ - } -} - -void -dfcountrefs(Memblk *f) -{ - Memblk *b; - int i; - - isfile(f); - if((f->addr&Fakeaddr) == 0 && f->addr >= fs->limit){ - fprint(2, "fscheck: '%s' d%#010ullx: out of range\n", - f->mf->name, f->addr); - return; - } - if((f->addr&Fakeaddr) == 0) - if(countref(f->addr) != 0) /* already visited */ - return; /* skip children */ - rwlock(f, Rd); - if(catcherror()){ - fprint(2, "fscheck: '%s' d%#010ullx: data: %r\n", - f->mf->name, f->addr); - rwunlock(f, Rd); - return; - } - for(i = 0; i < nelem(f->d.dptr); i++) - ptrmap(f->d.dptr[i], 0, bcountrefs, Disk); - for(i = 0; i < nelem(f->d.iptr); i++) - ptrmap(f->d.iptr[i], i+1, bcountrefs, Disk); - if(f->mf->mode&DMDIR) - for(i = 0; i < f->mf->length/sizeof(Dentry); i++){ - b = dfchild(f, i); - if(b == nil) - continue; - if(catcherror()) - fprint(2, "fscheck: '%s' d%#010ullx:" - " child[%d]: %r\n", - f->mf->name, f->addr, i); - else{ - dfcountrefs(b); - noerror(); - } - mbput(b); - } - noerror(); - rwunlock(f, Rd); -} - -/* - * Drop one disk reference for f and reclaim its storage if it's gone. - * The given memory reference is not released. - * For directories, all files contained have their disk references adjusted, - * and they are also reclaimed if no further references exist. - */ -int -dfreclaim(Memblk *f) -{ - int i; - Memblk *b; - long tot; - - isfile(f); - dKprint("dfreclaim %H\n", f); - /* - * Remove children if it's the last disk ref before we drop data blocks. - * No new disk refs may be added, so there's no race here. - */ - tot = 0; - if(dbgetref(f->addr) == 1 && (f->mf->mode&DMDIR) != 0){ - rwlock(f, Wr); - if(catcherror()){ - rwunlock(f, Wr); - error(nil); - } - for(i = 0; i < f->mf->length/sizeof(Dentry); i++){ - b = dfchild(f, i); - if(b == nil) - continue; - if(!catcherror()){ - tot += dfreclaim(b); - noerror(); - } - mbput(b); - } - noerror(); - rwunlock(f, Wr); - } - - if(dbput(f, f->type, f->addr) == 0) - tot++; - return tot; -} diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/fns.h --- a/sys/src/cmd/creepy/fns.h Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/fns.h Fri Mar 16 16:01:01 2012 +0100 @@ -1,40 +1,40 @@ extern Path* addelem(Path **pp, Memblk *f); -extern u64int addrofref(u64int refaddr, int idx); +extern daddrt addrofref(daddrt refaddr, int idx); extern void afree(Alloc *a, void *nd); +extern int allowed(int uid); +extern int allowed(int); +extern int allowed(int); extern void* anew(Alloc *a); extern void changed(Memblk *b); -extern void checktag(u64int tag, uint type, u64int addr); -extern void clearusers(void); +extern void checktag(u64int tag, uint type, daddrt addr); extern char* cliworker9p(void *v, void**aux); -extern char* cliworkerix(void *v, void**aux); 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 *ubuf, long count); extern Memblk* dballoc(uint type); -extern void dbclear(u64int tag, int type, u64int addr); extern Memblk* dbdup(Memblk *b); -extern Memblk* dbget(uint type, u64int addr); -extern u64int dbgetref(u64int addr); -extern u64int dbincref(u64int addr); -extern u64int dbput(Memblk *b, int type, u64int addr); +extern Memblk* dbget(uint type, daddrt addr); +extern daddrt dbgetref(daddrt addr); +extern daddrt dbput(Memblk *b, int type, daddrt addr); extern long dbread(Memblk *b); -extern void dbsetref(u64int addr, int ref); +extern void dbsetref(daddrt addr, int ref); extern long dbwrite(Memblk *b); extern void debug(void); -extern void dfaccessok(Memblk *f, char *uid, int bits); +extern void dfaccessok(Memblk *f, int 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 void dfchanged(Path *p); -extern u64int dfchdentry(Memblk *d, u64int addr, u64int naddr, int iswr); +extern void dfchanged(Path *p, int muid); +extern uvlong dfchdentry(Memblk *d, u64int addr, u64int naddr, int mkit); extern Memblk* dfchild(Memblk *f, int n); extern void dfcountfree(void); -extern void dfcountrefs(Memblk *f); -extern Memblk* dfcreate(Memblk *parent, char *name, char *uid, ulong mode); +extern long dfcountrefs(Memblk *f); +extern Memblk* dfcreate(Memblk *parent, char *name, int uid, ulong mode); extern void dfdropblks(Memblk *f, ulong bno, ulong bend); extern int dfdump(Memblk *f, int isdisk); extern int dffreeze(Memblk *f); +extern Path* dflast(Path **pp, int nth); extern void dflink(Memblk *d, Memblk *f); extern Path* dfmelt(Path **pp, int nth); extern ulong dfpread(Memblk *f, void *a, ulong count, uvlong off); @@ -43,10 +43,9 @@ extern int dfreclaim(Memblk *f); extern void dfremove(Memblk *p, Memblk *f); extern Blksl dfslice(Memblk *f, ulong len, uvlong off, int iswr); -extern void dfunlink(Memblk *d, Memblk *f); +extern void dfused(Path *p); 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 dumpfids(void); extern void dumplockstats(void); @@ -57,47 +56,40 @@ extern Fid* fidclone(Cli *cli, Fid *fid, int no); 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 void fidrahead(Fid *fid, uvlong offset); extern long fidread(Fid *fid, void *data, ulong count, vlong offset, Packmeta pack); extern void fidremove(Fid *fid); -extern void fidwahead(Fid *fid); extern void fidwalk(Fid *fid, char *wname); extern long fidwrite(Fid *fid, void *data, ulong count, uvlong *offset); extern void freerpc(Rpc *rpc); -extern void fscheck(void); +extern int fscheck(void); extern void fsdump(int full, int disktoo); extern void fsfmt(char *dev); -extern Memblk* fsfreeze(void); extern int fsfull(void); extern int fslru(void); extern uvlong fsmemfree(void); extern void fsopen(char *dev); extern void fspolicy(void); extern int fsreclaim(void); -extern void fsstats(int); extern void fssync(void); extern Fid* getfid(void* clino, int no); -extern Lstat* getlstat(uintptr pc, int type); -extern void gmeta(Fmeta *meta, void *buf, ulong nbuf); -extern Memblk* hosttodisk(Memblk *b); -extern void isdir(Memblk *f); +extern void gmeta(Memblk *f, void *buf, ulong nbuf); 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 int ixcallfmt(Fmt *fmt); extern uint ixpack(IXcall *f, uchar *ap, uint nap); extern uint ixpackedsize(IXcall *f); -extern void ixstats(int clr); +extern char* ixstats(char *s, char *e, int clr); +extern char* ixstats(char *s, char*, int); +extern char* ixstats(char *s, char*, int); extern uint ixunpack(uchar *ap, uint nap, IXcall *f); -extern void listen9pix(char *addr, char* (*cliworker)(void *arg, void **aux)); +extern int leader(int gid, int lead); extern void lockstats(int on); -extern Memblk* mballoc(u64int addr); +extern Memblk* mballoc(daddrt addr); extern int mbfmt(Fmt *fmt); -extern Memblk* mbget(int type, u64int addr, int mkit); +extern Memblk* mbget(int type, daddrt addr, int mkit); extern Memblk* mbhash(Memblk *b); extern void mbput(Memblk *b); extern void mbunhash(Memblk *b, int isreclaim); @@ -106,43 +98,50 @@ extern void meltfids(void); extern void meltfids(void); extern void meltfids(void); -extern int member(char *uid, char *member); -extern int member(char *uid, char *member); -extern int member(char *uid, char *member); -extern Memblk* mfchild(Memblk *f, int n); +extern int member(int uid, int member); +extern int member(int uid, int member); +extern int member(int uid, int member); +extern List mfilter(List *bl, int(*f)(Memblk*)); extern void mlistdump(char *tag, List *l); extern void munlink(List *l, Memblk *b, int isreclaim); -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(int clr); +extern char* ninestats(char *s, char *e, int clr); +extern char* ninestats(char *s, char*, int); +extern char* ninestats(char *s, char*, int); extern void nodebug(void); -extern uvlong now(void); -extern void okaddr(u64int addr); -extern void okdiskaddr(u64int addr); extern void ownpath(Path **pp); -extern void parseusers(char *u); -extern ulong pmeta(void *buf, ulong nbuf, Fmeta *meta); +extern int pathfmt(Fmt *fmt); +extern ulong pmeta(void *buf, ulong nbuf, Memblk *f); extern void putcli(Cli *cli); extern void putfid(Fid *fid); extern void putpath(Path *p); -extern u64int refaddr(u64int addr, int *idx); +extern void quiescent(int y); +extern void rahead(Memblk *f, uvlong offset); extern void replied(Rpc *rpc); extern void rlsedebug(int r); extern int rpcfmt(Fmt *fmt); extern void rwlock(Memblk *f, int iswr); extern void rwunlock(Memblk *f, int iswr); +extern void rwusers(Memblk *uf); +extern void rwusers(Memblk*); +extern void rwusers(Memblk*); extern int setdebug(void); -extern void srv9pix(char *srv, char* (*cliworker)(void *arg, void **aux)); extern char* tname(int t); +extern char* updatestats(int clr); +extern int usrfmt(Fmt *fmt); +extern int usrid(char *n); +extern int usrid(char*); +extern int usrid(char*); +extern char* usrname(int uid); +extern char* usrname(int); +extern char* usrname(int); +extern void wahead(Memblk *f); +extern int writedenied(int uid); extern void written(Memblk *b); extern void xqlock(QLock *q); extern void xqunlock(QLock *q); extern void xrwlock(RWLock *rw, int iswr); extern void xrwunlock(RWLock *rw, int iswr); -extern long watime(Memblk *f, void *buf, long); -extern long wid(Memblk *f, void *buf, long); -extern long wmtime(Memblk *f, void *buf, long); extern long wname(Memblk *f, void *buf, long len); diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/fscmd.c --- a/sys/src/cmd/creepy/fscmd.c Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/fscmd.c Fri Mar 16 16:01:01 2012 +0100 @@ -31,9 +31,27 @@ static int verb; int -member(char *uid, char *member) +member(int uid, int member) { - return strcmp(uid, member); + return uid == member; +} + +int +allowed(int) +{ + return 1; +} + +int +usrid(char*) +{ + return 0; +} + +char* +usrname(int) +{ + return getuser(); } void @@ -41,6 +59,23 @@ { } +void +rwusers(Memblk*) +{ +} + +char* +ninestats(char *s, char*, int) +{ + return s; +} + +char* +ixstats(char *s, char*, int) +{ + return s; +} + /* * Walks elems starting at f. * Ok if nelems is 0. @@ -59,14 +94,14 @@ } isfile(f); for(i = 0; i < nelems; i++){ - if((f->mf->mode&DMDIR) == 0) + if((f->d.mode&DMDIR) == 0) error("not a directory"); rwlock(f, Rd); if(catcherror()){ rwunlock(f, Rd); error("walk: %r"); } - nf = dfwalk(f, elems[i], 0); + nf = dfwalk(f, elems[i], Rd); rwunlock(f, Rd); addelem(&p, nf); mbput(nf); @@ -166,7 +201,7 @@ rwunlock(m, Wr); error(nil); } - f = dfcreate(m, fn, d->uid, d->mode&(DMDIR|0777)); + f = dfcreate(m, fn, usrid(d->uid), d->mode&(DMDIR|0777)); noerror(); addelem(&p, f); decref(f); /* kept now in p */ @@ -179,7 +214,8 @@ if((d->mode&DMDIR) == 0){ off = 0; for(;;){ - fslru(); + if(fsmemfree() < Mminfree) + fslru(); nr = read(fd, buf, sizeof buf); if(nr <= 0) break; @@ -219,11 +255,12 @@ } 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){ + m->name, (ulong)f->d.mode, f->d.length, m->uid, dbgetref(f->addr)); + if((f->d.mode&DMDIR) == 0){ off = 0; for(;;){ - fslru(); + if(fsmemfree() < Mminfree) + fslru(); nr = dfpread(f, buf, sizeof buf, off); if(nr <= 0) break; @@ -264,11 +301,12 @@ } 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){ + m->name, (ulong)f->d.mode, f->d.length, m->uid, dbgetref(f->addr)); + if((f->d.mode&DMDIR) == 0){ off = 0; for(;;){ - fslru(); + if(fsmemfree() < Mminfree) + fslru(); nr = dfpread(f, buf, sizeof buf, off); if(nr <= 0) break; @@ -350,7 +388,7 @@ static void fsst(int, char**) { - fsstats(0); + fprint(2, "%s\n", updatestats(0)); } static void @@ -360,6 +398,18 @@ } static void +fserr(int, char *argv[]) +{ + if(*argv[0] == 'r'){ + swreaderr = atoi(argv[1]); + print("sw read err count = %d\n", swreaderr); + }else{ + swwriteerr = atoi(argv[1]); + print("sw write err count = %d\n", swwriteerr); + } +} + +static void usage(void) { fprint(2, "usage: %s [-DFLAGS] [-dv] [-f disk] cmd...\n", argv0); @@ -384,6 +434,8 @@ {"rm", fsrm, 2, "rm!what"}, {"stats", fsst, 1, "stats"}, {"check", fschk, 1, "check"}, + {"rerr", fserr, 2, "rerr!n"}, + {"werr", fserr, 2, "werr!n"}, }; void @@ -410,6 +462,7 @@ }ARGEND; if(argc == 0) usage(); + fatalaborts = 1; fmtinstall('H', mbfmt); fmtinstall('M', dirmodefmt); errinit(Errstack); @@ -420,7 +473,7 @@ if(catcherror()) fatal("cmd %s: %r", argv[i]); if(verb>1) - fsdump(0, 0); + fsdump(0, Mem); print("%% %s\n", argv[i]); nargs = gettokens(argv[i], args, Nels, "!"); for(j = 0; j < nelem(cmds); j++){ @@ -442,7 +495,7 @@ } } if(verb>1) - fsdump(0, 0); + fsdump(0, Mem); noerror(); exits(nil); } diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/fsfmt.c --- a/sys/src/cmd/creepy/fsfmt.c Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/fsfmt.c Fri Mar 16 16:01:01 2012 +0100 @@ -13,9 +13,27 @@ #include "fns.h" int -member(char *uid, char *member) +usrid(char*) { - return strcmp(uid, member); + return 3; +} + +char* +usrname(int) +{ + return "sys"; +} + +int +member(int uid, int member) +{ + return uid == member; +} + +int +allowed(int) +{ + return 1; } void @@ -23,6 +41,23 @@ { } +void +rwusers(Memblk*) +{ +} + +char* +ninestats(char *s, char*, int) +{ + return s; +} + +char* +ixstats(char *s, char*, int) +{ + return s; +} + static void usage(void) { @@ -46,6 +81,7 @@ if((ARGC() >= 'A' && ARGC() <= 'Z') || ARGC() == '9'){ dbg['d'] = 1; dbg[ARGC()] = 1; + fatalaborts = 1; }else usage(); }ARGEND; @@ -60,7 +96,7 @@ fatal("error: %r"); fsfmt(dev); if(verb) - fsdump(0, 0); + fsdump(0, Mem); noerror(); exits(nil); } diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/fsys.c --- a/sys/src/cmd/creepy/fsys.c Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/fsys.c Fri Mar 16 16:01:01 2012 +0100 @@ -37,6 +37,17 @@ [Write] "write", }; +char statstext[Statsbufsz], *statsp; + +void +quiescent(int y) +{ + if(y == No) + xrwlock(&fs->quiescence, Rd); + else + xrwunlock(&fs->quiescence, Rd); +} + static uvlong fsdiskfree(void) { @@ -49,52 +60,82 @@ return nfree; } -void -fsstats(int) +static char* +fsstats(char *s, char *e, int clr) { int i; - fprint(2, "mblks:\t%4uld nblk %4uld nablk %4uld mused %4uld mfree\n", + s = seprint(s, e, "mblks:\t%4uld nblk %4uld nablk %4uld mused %4uld mfree\n", fs->nblk, fs->nablk, fs->nmused, fs->nmfree); - fprint(2, "lists:\t%4uld lru %#4uld dirty %#4uld refs %4uld total\n", + s = seprint(s, e, "lists:\t%4uld lru %#4uld dirty %#4uld refs %4uld total\n", fs->lru.n, fs->mdirty.n, fs->refs.n, fs->lru.n + fs->mdirty.n + fs->refs.n); - fprint(2, "dblks:\t %4ulld dtot %4ulld dfree (%ulld list + %ulld rem)\n", + s = seprint(s, e, "dblks:\t %4ulld dtot %4ulld dfree (%ulld list + %ulld rem)\n", fs->limit/Dblksz - 1, fsdiskfree(), fs->super->d.ndfree, (fs->limit - fs->super->d.eaddr)/Dblksz); - fprint(2, "paths:\t%4uld alloc %4uld free (%4uld bytes)\n", + s = seprint(s, e, "paths:\t%4uld alloc %4uld free (%4uld bytes)\n", pathalloc.nalloc, pathalloc.nfree, pathalloc.elsz); - fprint(2, "mfs:\t%4uld alloc %4uld free (%4uld bytes)\n", + s = seprint(s, e, "mfs:\t%4uld alloc %4uld free (%4uld bytes)\n", mfalloc.nalloc, mfalloc.nfree, mfalloc.elsz); - fprint(2, "nmelts:\t%d\n", fs->nmelts); - fprint(2, "nindirs:\t"); - for(i = 0; i < nelem(fs->nindirs); i++) - fprint(2, "%d ", fs->nindirs[i]); - fprint(2, "\n"); - fprint(2, "\n"); - fprint(2, "Fsysmem:\t%uld\n", Fsysmem); - fprint(2, "Dminfree:\t%d\n", Dminfree); - fprint(2, "Dblksz: \t%uld\n", Dblksz); - fprint(2, "Mblksz: \t%ud\n", sizeof(Memblk)); - fprint(2, "Dminattrsz:\t%uld\n", Dminattrsz); - fprint(2, "Nblkgrpsz:\t%uld\n", Nblkgrpsz); - fprint(2, "Dblkdatasz:\t%d\n", Dblkdatasz); - fprint(2, "Embedsz:\t%d\n", Embedsz); - fprint(2, "Dentryperblk:\t%d\n", Dblkdatasz/sizeof(Dentry)); - fprint(2, "Dptrperblk:\t%d\n\n", Dptrperblk); + s = seprint(s, e, "nmelts:\t%d\n", fs->nmelts); + s = seprint(s, e, "nindirs:\t"); + for(i = 0; i < nelem(fs->nindirs); i++){ + s = seprint(s, e, "%d ", fs->nindirs[i]); + if(clr) + fs->nindirs[i] = 0; + } + s = seprint(s, e, "\n"); + s = seprint(s, e, "\n"); + s = seprint(s, e, "Fsysmem:\t%uld\n", Fsysmem); + s = seprint(s, e, "Mzerofree:\t%d\tMminfree:\t%d\tMmaxfree:\t%d\n", + Mzerofree, Mminfree, Mmaxfree); + s = seprint(s, e, "Dzerofree:\t%d\tDminfree:\t%d\tDmaxfree:\t%d\n", + Dzerofree, Dminfree, Dmaxfree); + s = seprint(s, e, "Mmaxdirtypcent:\t%d\n", Mmaxdirtypcent); + s = seprint(s, e, "Dblksz: \t%uld\n", Dblksz); + s = seprint(s, e, "Mblksz: \t%ud\n", sizeof(Memblk)); + s = seprint(s, e, "Dminattrsz:\t%uld\n", Dminattrsz); + s = seprint(s, e, "Nblkgrpsz:\t%uld\n", Nblkgrpsz); + s = seprint(s, e, "Dblkdatasz:\t%d\n", Dblkdatasz); + s = seprint(s, e, "Embedsz:\t%d\n", Embedsz); + s = seprint(s, e, "Dentryperblk:\t%d\n", Dblkdatasz/Daddrsz); + s = seprint(s, e, "Dptrperblk:\t%d\n\n", Dptrperblk); - for(i = 0; i < nelem(nfsopcalls); i++) + for(i = 0; i < nelem(nfsopcalls); i++){ if(nfsopcalls[i] == 0) - fprint(2, "%s:\t0 calls\t0 µs\n", fsopname[i]); + s = seprint(s, e, "%s:\t0 calls\t0 µs\n", fsopname[i]); else - fprint(2, "%s:\t%uld calls\t%ulld µs\n", fsopname[i], + s = seprint(s, e, "%s:\t%uld calls\t%ulld µs\n", fsopname[i], nfsopcalls[i], (fsoptime[i]/nfsopcalls[i])/1000); + if(clr){ + nfsopcalls[i] = 0; + fsoptime[i] = 0; + } + } + return s; +} + +char* +updatestats(int clr) +{ + static QLock statslk; + + if(clr) + fprint(2, "%s: clearing stats\n", argv0); + xqlock(&statslk); + statsp = statstext; + *statsp = 0; + statsp = fsstats(statsp, statstext+sizeof statstext, clr); + statsp = ninestats(statsp, statstext+sizeof statstext, clr); + statsp = ixstats(statsp, statstext+sizeof statstext, clr); + xqunlock(&statslk); + return statstext; } int isro(Memblk *f) { - return f == fs->archive || f == fs->root || f == fs->cons; + return f == fs->archive || f == fs->root || f == fs->cons || f == fs->stats; } /* @@ -106,7 +147,7 @@ { int i, n, x; Memblk *b; - u64int a; + daddrt a; extern int fullfiledumps; x = fullfiledumps; @@ -142,36 +183,40 @@ } mlistdump("mru", &fs->lru); mlistdump("dirty", &fs->mdirty); - fsstats(0); + fprint(2, "%s\n", updatestats(0)); fullfiledumps = x; debug(); } /* - * NO LOCKS: - * The disk FS should be quiescent. - * * Failed checks are reported but not fixed (but for leaked blocks). * The user is expected to format the partition and restore contents from venti. * We might easily remove the dir entries for corrupt files, and restore */ -void +int fscheck(void) { long i; - u64int n, addr; + daddrt n, addr; + long nfails; + xqlock(&fs->fzlk); + xrwlock(&fs->quiescence, Wr); + nfails = 0; if(fs->chk == nil) fs->chk = mallocz(fs->ndblk, 1); else memset(fs->chk, 0, fs->ndblk); if(catcherror()){ + xrwunlock(&fs->quiescence, Wr); + xqunlock(&fs->fzlk); fprint(2, "fscheck: %r\n"); - return; + nfails++; + return nfails; } fprint(2, "%s: checking %s...\n", argv0, fs->dev); - dfcountrefs(fs->root); + nfails += dfcountrefs(fs->root); dprint("countfree...\n"); dfcountfree(); @@ -184,28 +229,38 @@ dbsetref(addr, fs->super->d.free); fs->super->d.free = addr; noerror(); + }else{ + fprint(2, "%s: check: %r\n", argv0); + nfails++; } continue; } if(fs->chk[i] == 0xFF) continue; n = dbgetref(addr); - if(fs->chk[i] == 0xFE && n < (u64int)0xFE) + if(fs->chk[i] == 0xFE && n < (daddrt)0xFE){ fprint(2, "fscheck: d%#010ullx: found >%ud != ref %ulld\n", addr, fs->chk[i], n); - if(fs->chk[i] < 0xFE && n != fs->chk[i]) + nfails++; + } + if(fs->chk[i] < 0xFE && n != fs->chk[i]){ fprint(2, "fscheck: d%#010ullx: found %ud != ref %ulld\n", addr, fs->chk[i], n); + nfails++; + } } + xrwunlock(&fs->quiescence, Wr); + xqunlock(&fs->fzlk); noerror(); fprint(2, "%s: %s check complete\n", argv0, fs->dev); + return nfails; } -static usize +static daddrt disksize(int fd) { Dir *d; - u64int sz; + daddrt sz; d = dirfstat(fd); if(d == nil) @@ -292,14 +347,14 @@ /* * Freeze the file tree, keeping active as a new melted file * that refers to frozen children now in the archive. - * returns the just frozen tree. + * returns the just frozen tree or nil * * This requires two or three free blocks: * - one free block to dup the new active * - one to freeze the super block * - an extra ref block if the new blocks come from a new block group. */ -Memblk* +static Memblk* fsfreeze(void) { Memblk *na, *oa, *arch; @@ -310,6 +365,14 @@ dprint("freezing fs...\n"); t0 = nsec(); xqlock(&fs->fzlk); + if(fs->fzsuper != nil){ + /* + * we did freeze/reclaim and are still writing, can't freeze now. + */ + xqunlock(&fs->fzlk); + return nil; + } + xrwlock(&fs->quiescence, Wr); /* not really required */ nfsopcalls[Freeze]++; if(catcherror()){ /* @@ -330,13 +393,16 @@ /* * move active into /archive/. */ - seprint(name, name+sizeof(name), "%ulld", oa->mf->mtime); + seprint(name, name+sizeof(name), "%ulld", oa->d.mtime); wname(oa, name, strlen(name)+1); dflink(arch, oa); /* 1. Freeze the entire previously active. */ + oa->d.mtime = t0; + oa->d.atime = t0; rwunlock(oa, Wr); /* race */ + changed(oa); dffreeze(oa); rwunlock(arch, Wr); @@ -351,12 +417,12 @@ na = dbdup(oa); rwlock(na, Wr); id = nsec(); - wid(na, &id, sizeof id); + na->d.id = nsec(); wname(na, "active", strlen("active")+1); fs->active = na; - dfchdentry(fs->root, oa->addr, na->addr, 1); + dfchdentry(fs->root, oa->addr, na->addr, Mkit); assert(oa->ref > 1); /* release fs->active */ mbput(oa); @@ -370,6 +436,7 @@ meltfids(); fsoptime[Freeze] += nsec() - t0; + xrwunlock(&fs->quiescence, Wr); xqunlock(&fs->fzlk); noerror(); return na; @@ -392,28 +459,29 @@ return n; } +static int +mustwrite(Memblk *b) +{ + return b->frozen != 0 || b == fs->archive || b->aflag != 0; +} + /* * Written blocks become mru, perhaps we should * consider keeping their location in the lru list, at the * expense of visiting them while scanning for blocks to move out. + * We write only (dirty) blocks that are frozen or part of the "/archive" file. */ static long writedata(void) { - Memblk *b, *nb; + Memblk *b; long nw; + List dl; nw = 0; - qlock(&fs->mdirty); - b = fs->mdirty.hd; - fs->mdirty.hd = nil; - fs->mdirty.tl = nil; - fs->mdirty.n = 0; - qunlock(&fs->mdirty); - for(; b != nil; b = nb){ - nb = b->lnext; - b->lnext = nil; - b->lprev = nil; + dl = mfilter(&fs->mdirty, mustwrite); + while((b = dl.hd) != nil){ + munlink(&dl, b, 1); assert(b->dirty); if((b->addr&Fakeaddr) != 0) fatal("write data on fake address"); @@ -436,7 +504,7 @@ } static void -syncref(u64int addr) +syncref(daddrt addr) { static Memblk b; @@ -495,7 +563,8 @@ nb++; syncrefs(); noerror(); - fsoptime[Write] += nsec() - t0; + fs->wtime = nsec(); + fsoptime[Write] += fs->wtime - t0; xqunlock(&fs->fzlk); dprint("fs written (2*%ld refs %ld data)\n", nr, nb); } @@ -506,6 +575,9 @@ uvlong fact; int i; + /* this is an invariant that must hold for directories */ + assert(Embedsz % Daddrsz == 0); + maxfsz = Ndptr*Dblkdatasz; fact = 1; for(i = 0; i < Niptr; i++){ @@ -539,12 +611,8 @@ void fssync(void) { - /* - * TODO: If active has not changed and we are just going - * to dump a new archive for no change, do nothing. - */ - fsfreeze(); - fswrite(); + if(fsfreeze()) + fswrite(); } /* @@ -558,6 +626,7 @@ fsfmt(char *dev) { Memblk *super; + int uid; fsinit(dev, Mmaxfree); /* enough # of blocks for fmt */ @@ -576,10 +645,12 @@ super->d.dblkdatasz = Dblkdatasz; super->d.embedsz = Embedsz; super->d.dptrperblk = Dptrperblk; - fs->root = dfcreate(nil, "", getuser(), DMDIR|0555); + uid = usrid(getuser()); + fs->root = dfcreate(nil, "", uid, DMDIR|0555); rwlock(fs->root, Wr); - fs->active = dfcreate(fs->root, "active", getuser(), DMDIR|0775); - fs->archive = dfcreate(fs->root, "archive", getuser(), DMDIR|0555); + fs->active = dfcreate(fs->root, "active", uid, DMDIR|0775); + fs->archive = dfcreate(fs->root, "archive", uid, DMDIR|0555); + fs->archive->aflag = 1; rwunlock(fs->root, Wr); super->d.root = fs->archive->addr; fssync(); @@ -592,13 +663,11 @@ * for the cache. * To open more file systems, use more processes! */ - void fsopen(char *dev) { Memblk *arch, *last, *c; - u64int id; - int i; + int i, uid; if(catcherror()) fatal("fsopen: error: %r"); @@ -606,15 +675,17 @@ fsinit(dev, 0); readsuper(); + uid = usrid("sys"); xqlock(&fs->fzlk); - fs->root = dfcreate(nil, "", getuser(), DMDIR|0555); + fs->root = dfcreate(nil, "", uid, DMDIR|0555); arch = dbget(DBfile, fs->super->d.root); + arch->aflag = 1; fs->archive = arch; rwlock(fs->root, Wr); rwlock(arch, Wr); last = nil; for(i = 0; (c = dfchild(arch, i)) != nil; i++){ - if(last == nil || last->mf->mtime < c->mf->mtime){ + if(last == nil || last->d.mtime < c->d.mtime){ mbput(last); last = c; incref(c); @@ -627,22 +698,48 @@ mbput(last->mf->melted); /* could keep it, but no need */ last->mf->melted = nil; wname(fs->active, "active", strlen("active")+1); - id = nsec(); - wid(fs->active, &id, sizeof id); + fs->active->d.id = nsec(); rwlock(fs->active, Wr); dflink(fs->root, fs->active); rwunlock(fs->active, Wr); rwunlock(last, Rd); mbput(last); }else - fs->active = dfcreate(fs->root, "active", getuser(), DMDIR|0775); + fs->active = dfcreate(fs->root, "active", uid, DMDIR|0775); dflink(fs->root, arch); rwunlock(arch, Wr); - fs->cons = dfcreate(nil, "cons", getuser(), DMEXCL|600); + fs->cons = dfcreate(nil, "cons", uid, DMEXCL|0660); + fs->cons->d.gid = usrid("adm"); + fs->cons->mf->gid = "adm"; + fs->stats = dfcreate(nil, "stats", uid, 0664); + changed(fs->cons); fs->consc = chancreate(sizeof(char*), 256); rwunlock(fs->root, Wr); xqunlock(&fs->fzlk); + noerror(); + + /* + * Try to load the /active/users file, if any, + * but ignore errors. We already have a default table loaded + * and may operate using it. + */ + if(!catcherror()){ + c = dfwalk(fs->active, "users", Rd); + rwlock(c, Wr); + if(catcherror()){ + rwunlock(c, Wr); + mbput(c); + error(nil); + } + rwusers(c); + noerror(); + rwunlock(c, Wr); + mbput(c); + noerror(); + fs->cons->d.uid = usrid(getuser()); + fs->cons->mf->uid = getuser(); + } } uvlong @@ -670,22 +767,17 @@ int x; long target, tot, n, ign; - if(fsmemfree() > Mminfree) - return 0; x = setdebug(); dprint("fslru: low on memory %ulld free %d min\n", fsmemfree(), Mminfree); tot = ign = 0; do{ target = Mmaxfree - fsmemfree(); t0 = nsec(); - if(!canqlock(&fs->fzlk)) /* we'll get called later */ - break; xqlock(&fs->lru); nfsopcalls[Lru]++; if(catcherror()){ fsoptime[Lru] += t0 - nsec(); xqunlock(&fs->lru); - xqunlock(&fs->fzlk); fprint(2, "%s: fslru: %r\n", argv0); break; } @@ -727,7 +819,6 @@ noerror(); fsoptime[Lru] += t0 - nsec(); xqunlock(&fs->lru); - xqunlock(&fs->fzlk); }while(n > 0 && target > 0); if(tot == 0){ fprint(2, "%s: low on mem (0 out; %uld ignored)\n", argv0, ign); @@ -752,7 +843,7 @@ if(1){ fprint(2, "file system full:\n"); - fsdump(0, 0); + fsdump(0, Mem); fatal("aborting"); } return 1; @@ -763,13 +854,21 @@ { Memblk *arch, *c, *victim; int i; - u64int addr; + daddrt addr; Blksl sl; - Dentry *de; long n, tot; + xqlock(&fs->fzlk); fprint(2, "%s: %ulld free: reclaiming...\n", argv0, fsdiskfree()); - xqlock(&fs->fzlk); + if(fs->fzsuper != nil){ + /* + * we did freeze/reclaim and are still writing, can't reclaim now. + */ + xqunlock(&fs->fzlk); + fprint(2, "%s: writing, skip reclaim\n", argv0); + return 0; + } + arch = fs->archive; rwlock(arch, Wr); if(catcherror()){ @@ -784,7 +883,7 @@ for(i = 0; (c = dfchild(arch, i)) != nil; i++){ if(victim == nil) victim = c; - else if(victim->mf->mtime > c->mf->mtime){ + else if(victim->d.mtime > c->d.mtime){ mbput(victim); victim = c; }else @@ -796,33 +895,32 @@ dprint("nothing to reclaim\n"); break; } + fprint(2, "%s: reclaiming /archive/%s\n", argv0, victim->mf->name); dprint("victim is %H\n", victim); + addr = dfchdentry(arch, victim->addr, 0, Dontmk); /* - * Don't make a new archive. Edit in-place the one we have to - * clear the reference to the victim. + * Write the new /archive without the file reclaimed, in + * case we fail while reclaiming. */ - addr = dfchdentry(arch, victim->addr, 0, 0); - assert(addr != Noaddr); - sl = dfslice(arch, sizeof(Dentry), addr, 0); - assert(sl.b); - if(catcherror()){ - mbput(sl.b); - error(nil); + sl = dfslice(arch, sizeof(u64int), addr, Rd); + if(sl.b != nil && sl.b != arch){ + munlink(&fs->mdirty, sl.b, 0); + if(!catcherror()){ + dbwrite(sl.b); + noerror(); + } } - de = sl.data; - de->file = 0; - changed(sl.b); - munlink(&fs->mdirty, sl.b, 0); - dbwrite(sl.b); - noerror(); mbput(sl.b); + changed(arch); + munlink(&fs->mdirty, arch, 0); + dbwrite(arch); n = dfreclaim(victim); mbput(victim); - fs->super->d.root = fs->archive->addr; + fs->super->d.root = arch->addr; dprint("fsreclaim: %uld file%s reclaimed\n", n, n?"s":""); tot += n; @@ -845,31 +943,86 @@ return tot; } +static int +fsdirtypcent(void) +{ + long n, ndirty; + + n = fs->lru.n; + ndirty = fs->mdirty.n; + + return (ndirty*100)/(n + ndirty); +} + /* * Policy for memory and and disk block reclaiming. - * Should be called from time to time to guarantee that there are - * free blocks. - * - * If low on memory, move some blocks out. - * If we can't, sync to make some available the next time. - * Either way, reclaim old freezes if low on disk, but don't - * do that if we are low on memory, because that might - * require loading disk blocks. + * Should be called from time to time to guarantee that there are free blocks. */ void fspolicy(void) { - switch(fslru()){ - case -1: /* wanted blocks; lru had none */ + int lomem, lodisk, hidirty, longago; + + /* + * XXX: This will call fswrite() while blocking everyone calling + * fspolicy(), including all rpcs. + * + * That's done so now because the sizes used for testing are a joke, + * so that a single rpc may fill the disk even while another is already + * running the policy!, despite water marks! + * + * Once testing is complete, we should insist on running the policy + * (qlock it) only if we are really low on resources, otherwise, we + * should canqlock or defer it for later (because the most likely + * reason we can't qlock it is that it's already running and perhaps + * writing the frozen fs to disk). + * + * If we forget to make that change, writes will become + * synchronous and that should be considered as a BUG. + * + */ + xqlock(&fs->policy); + if(catcherror()){ + xqunlock(&fs->policy); + fatal("fspolicy: %r"); /* should just print and return */ + } + + lomem = fsmemfree() < Mminfree; + lodisk = fsdiskfree() < Dminfree; + hidirty = fsdirtypcent() > Mmaxdirtypcent; + longago = (nsec() - fs->wtime)/1000000UL > Syncival; + + /* Ideal sequence for [lomem lodisk hidirty] might be: + * 111: lru sync reclaim+sync lru + * 110: lru reclaim+sync + * 101: lru sync lru + * 100: lru + * 011: reclaim+sync + * 010: reclaim+sync + * 001: sync + * 000: - + * Plus: if we are still low on memory, after lru, try + * doing a sync to move blocks to the lru list, ie. fake "hidirty". + */ + + if(lomem || lodisk || hidirty) + dprint("fspolicy: lomem=%d (%ulld) lodisk=%d (%ulld)" + " hidirty=%d (%d%%)\n", + lomem, fsmemfree(), lodisk, fsdiskfree(), + hidirty, fsdirtypcent()); + if(lomem){ + fslru(); + lomem = fsmemfree() < Mminfree; + if(lomem) + hidirty++; + } + if(lodisk) + fsreclaim(); + if(lodisk || hidirty || longago) fssync(); - break; - case 0: /* did not want blocks */ - default: /* wanted blocks; lru had some */ - if(fsdiskfree() < Dminfree){ - if(fsreclaim() > 0) - fssync(); - } - break; - } + if(lomem && hidirty) + fslru(); + + qunlock(&fs->policy); + noerror(); } - diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/ix.c --- a/sys/src/cmd/creepy/ix.c Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/ix.c Fri Mar 16 16:01:01 2012 +0100 @@ -75,16 +75,16 @@ static int ixrreadhdrsz; -void -ixstats(int clr) +char* +ixstats(char *s, char *e, int clr) { int i; - fprint(2, "srpcs:\t%4uld alloc %4uld free (%4uld bytes)\n", + s = seprint(s, e, "srpcs:\t%4uld alloc %4uld free (%4uld bytes)\n", srpcalloc.nalloc, srpcalloc.nfree, srpcalloc.elsz); for(i = 0; i < nelem(ixcalls); i++) if(ixcalls[i] != nil && ncalls[i] > 0){ - fprint(2, "%-8s\t%5uld calls\t%11ulld µs\n", + s = seprint(s, e, "%-8s\t%5uld calls\t%11ulld µs\n", callname[i], ncalls[i], (calltime[i]/ncalls[i])/1000); if(clr){ @@ -92,6 +92,7 @@ calltime[i] = 0; } } + return s; } @@ -212,7 +213,7 @@ return 0; if(catcherror()) return 0; - n = pmeta(buf+BIT32SZ, nbuf-BIT32SZ, f->mf); + n = pmeta(buf+BIT32SZ, nbuf-BIT32SZ, f); noerror(); PBIT32(buf, n); return n+BIT32SZ; @@ -308,7 +309,7 @@ fid = rpc->rpc0->fid; if(fid == nil) error("fid not set"); - p = fid->p; + p = dflast(&fid->p, fid->p->nf); f = p->f[p->nf-1]; rwlock(f, Rd); if(catcherror()){ @@ -328,11 +329,17 @@ Path *p; Memblk *f; + /* + * XXX: add checks like done in wstat(). + * this code is incomplete. + */ fid = rpc->rpc0->fid; if(fid == nil) error("fid not set"); p = fid->p; f = p->f[p->nf-1]; + if(writedenied(fid->uid)) + error("user can't write"); if(isro(f) || fid->archived) error("can't wattr archived or built-in files"); p = dfmelt(&fid->p, fid->p->nf); @@ -473,8 +480,8 @@ return -1; } if(chan&Tlast){ - putfid(rpc->rpc0->fid); - rpc->rpc0->fid = nil; + putfid(rpc->rpc0->fid); /* release rpc fid before replying */ + rpc->rpc0->fid = nil; /* or we might get "fid in use" errors */ } dXprint("-> %G\n", &rpc->xr); if(write(cli->fd, buf, p-buf) != p-buf){ @@ -495,9 +502,9 @@ Cli *cli; Channel *c; char err[128]; - long nw, count; + long nw; int nerr; - Fid *fid; + Memblk *fahead; c = v; if(*aux == nil){ @@ -511,11 +518,16 @@ cli = rpc->cli; threadsetname("rpcworkerix %s chan %d", cli->addr, rpc0->chan); dPprint("%s started\n", threadgetname()); + do{ + fspolicy(); + nerr = nerrors(); rpc->xr.type = rpc->xt.type + 1; rpc->rpc0 = rpc0; + quiescent(No); if(catcherror()){ + quiescent(Yes); rpc->xr.type = Rerror; rpc->xr.ename = err; rerrstr(err, sizeof err); @@ -523,43 +535,38 @@ rpc0->fid->cflags |= OCEND; }else{ ixcalls[rpc->xt.type](rpc); + quiescent(Yes); noerror(); } - fid = nil; - if(rpc->fid != nil && rpc0->fid->ref > 1){ - /* The fid is not clunked by this rpc; read/walk ahead ok*/ - fid = rpc0->fid; - incref(fid); - } + fahead = nil; + if(rpc0->fid != nil && rpc0->fid->p != nil) + if(rpc->xr.type == IXRread || rpc->xr.type == IXRwalk){ + fahead = rpc0->fid->p->f[rpc0->fid->p->nf - 1]; + incref(fahead); + } if(catcherror()){ - if(fid != nil) - putfid(fid); + mbput(fahead); + error(nil); } nw = reply(rpc); - if(fid != nil){ - switch(rpc->xt.type){ - case Tread: - count = rpc->xt.count; - if(rpc->xr.type == Rread) - if(rpc->xt.nmsg <= 1 && rpc->xr.count == count) - fidrahead(rpc0->fid, rpc->xt.offset+count); - break; - case Twalk: - if(rpc->xr.type == Rwalk) - fidwahead(rpc0->fid); - break; - } - putfid(fid); + if(fahead != nil){ + if(rpc->xr.type == IXRread && rpc->xt.nmsg <= 1) + rahead(fahead, rpc->xt.offset + rpc->xr.count); + else if(rpc->xr.type == IXRwalk) + wahead(fahead); + mbput(fahead); } + noerror(); if(rpc != rpc0) freeixrpc(rpc); if(nerrors() != nerr) fatal("%s: unbalanced error stack", threadgetname()); }while(!rpc0->closed && nw > 0 && err[0] == 0 && (rpc = recvp(c)) != nil); + while((rpc = nbrecvp(c)) != nil) freeixrpc(rpc); replied(rpc0); @@ -578,7 +585,7 @@ ixrreadhdrsz = ixpackedsize(&xt) + BIT16SZ + BIT16SZ; } -char* +static char* cliworkerix(void *v, void**aux) { Cli *cli; @@ -626,8 +633,6 @@ cli->nrpcs++; xqunlock(&cli->rpclk); - fspolicy(); - if(rpc->chan&Tlast) rpc->closed = 1; sendp(rpc->c, rpc); diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/mblk.c --- a/sys/src/cmd/creepy/mblk.c Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/mblk.c Fri Mar 16 16:01:01 2012 +0100 @@ -65,16 +65,16 @@ } int mbtab; static void -fmtptr(Fmt *fmt, int type, u64int addr, char *tag, int n) +fmtptr(Fmt *fmt, int type, daddrt addr, char *tag, int n) { Memblk *b; if(addr == 0) return; - b = mbget(type, addr, 0); + b = mbget(type, addr, Dontmk); if(b == nil){ fmttab(fmt, mbtab, 0); - fmtprint(fmt, " %s[%d] = d%#010ullx \n", tag, n, addr); + fmtprint(fmt, "%s[%d] = d%#010ullx \n", tag, n, addr); }else{ fmtprint(fmt, "%H", b); mbput(b); @@ -84,16 +84,16 @@ dumpdirdata(Fmt *fmt, Memblk *b) { long doff; - u64int *p; + daddrt *p; int i; - if(b->mf->length == 0 || (b->mf->mode&DMDIR) == 0) + if(b->d.length == 0 || (b->d.mode&DMDIR) == 0) return; doff = embedattrsz(b); if(doff < Embedsz){ fmttab(fmt, mbtab, 0); - p = (u64int*)(b->d.embed+doff); - for(i = 0; i < 5 && (uchar*)p < b->d.embed+Embedsz - BIT64SZ; i++) + p = (daddrt*)(b->d.embed+doff); + for(i = 0; i < 5 && (uchar*)p < b->d.embed+Embedsz - Daddrsz; i++) fmtprint(fmt, "%sd%#010ullx", i?" ":"data: ", EP(*p++)); fmtprint(fmt, "\n"); } @@ -154,17 +154,17 @@ fmtprint(fmt, " asz %#ullx aptr %#ullx", b->d.asize, b->d.aptr); fmttab(fmt, mbtab, 0); - fmtprint(fmt, " %M melted m%#p\n", - (ulong)b->mf->mode, b->mf->melted); + fmtprint(fmt, " %M '%s' len %ulld melted m%#p\n", + (ulong)b->d.mode, usrname(b->d.uid), b->d.length, b->mf->melted); if(0){ fmttab(fmt, mbtab, 0); fmtprint(fmt, " id %#ullx mode %M mt %#ullx" - " sz %#ullx '%s'\n", - EP(b->mf->id), (ulong)b->mf->mode, - EP(b->mf->mtime), b->mf->length, b->mf->uid); + " '%s'\n", + EP(b->d.id), (ulong)b->d.mode, + EP(b->d.mtime), b->mf->uid); } mbtab++; - if(b->mf->mode&DMDIR) + if(b->d.mode&DMDIR) dumpdirdata(fmt, b); for(i = 0; i < nelem(b->d.dptr); i++) fmtptr(fmt, DBdata, b->d.dptr[i], "d", i); @@ -208,7 +208,7 @@ void ismelted(Memblk *b) { - if(b != fs->archive && b->frozen) + if(b != fs->archive && b->aflag == 0 && b->frozen) fatal("frozen at pc %#p", getcallerpc(&b)); } @@ -261,6 +261,25 @@ xqunlock(l); } +List +mfilter(List *bl, int(*f)(Memblk*)) +{ + Memblk *b, *bnext; + List wl; + + memset(&wl, 0, sizeof wl); + xqlock(bl); + for(b = bl->hd; b != nil; b = bnext){ + bnext = b->lnext; + if(f(b)){ + munlink(bl, b, 1); + mlinklast(&wl, b); + } + } + xqunlock(bl); + return wl; +} + void mlistdump(char *tag, List *l) { @@ -310,6 +329,11 @@ ismelted(b); if(b->dirty || (b->addr&Fakeaddr) != 0) return; + lock(&b->dirtylk); + if(b->dirty){ + unlock(&b->dirtylk); + return; + } switch(b->type){ case DBsuper: case DBref: @@ -321,16 +345,19 @@ b->dirty = 1; mlink(&fs->mdirty, b); } + unlock(&b->dirtylk); } void written(Memblk *b) { + lock(&b->dirtylk); assert(b->dirty != 0); switch(b->type){ case DBsuper: case DBref: b->dirty = 0; + unlock(&b->dirtylk); break; default: /* @@ -340,7 +367,7 @@ */ assert(b->lprev == nil && b->lnext == nil); b->dirty = 0; - + unlock(&b->dirtylk); /* * heuristic: frozen files that have a melted version @@ -468,10 +495,10 @@ assert(mf->writer == 0 && mf->readers == 0); afree(&mfalloc, mf); } + b->addr = 0; b->type = DBfree; b->d.tag = DBfree; - b->frozen = b->dirty = 0; - b->addr = 0; + b->frozen = b->dirty = b->aflag = b->loading = b->changed = 0; xqlock(fs); fs->nmused--; @@ -482,10 +509,20 @@ } Memblk* -mballoc(u64int addr) +mballoc(daddrt addr) { Memblk *b; + if(fsmemfree() < Mzerofree){ + if(canqlock(&fs->lru)) + qunlock(&fs->lru); + else + fatal("mballoc: out of blocks and can't fslru()"); + if(!catcherror()){ + fslru(); + noerror(); + } + } b = nil; xqlock(fs); if(fs->free != nil){ @@ -496,6 +533,8 @@ b = &fs->blk[fs->nblk++]; else{ xqunlock(fs); + if(dbg['d']) + fsdump(1, Mem); fatal("mballoc: no free blocks"); } fs->nmused++; @@ -508,7 +547,7 @@ } Memblk* -mbget(int type, u64int addr, int mkit) +mbget(int type, daddrt addr, int mkit) { Memblk *b; uint hv; diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/mkfile --- a/sys/src/cmd/creepy/mkfile Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/mkfile Fri Mar 16 16:01:01 2012 +0100 @@ -11,10 +11,8 @@ mblk.$O\ dblk.$O\ fblk.$O\ - file.$O\ attr.$O\ fsys.$O\ - file.$O\ tools.$O\ IXOFILES=\ diff -r 5ff7711591da -r a23adee6c6a7 sys/src/cmd/creepy/tools.c --- a/sys/src/cmd/creepy/tools.c Fri Mar 16 15:57:01 2012 +0100 +++ b/sys/src/cmd/creepy/tools.c Fri Mar 16 16:01:01 2012 +0100 @@ -20,7 +20,7 @@ static Lstat none; static Lstat *lstats; static int lstatson; -int fatalaborts = 1; +int fatalaborts; Alloc pathalloc = { @@ -42,12 +42,6 @@ threadexitsall("fatal"); } -uvlong -now(void) -{ - return nsec(); -} - void lockstats(int on) { @@ -76,7 +70,7 @@ lstatson = lon; } -Lstat* +static Lstat* getlstat(uintptr pc, int type) { Lstat *lst; @@ -295,3 +289,17 @@ return p; } +int +pathfmt(Fmt *fmt) +{ + Path *p; + int i; + + + p = va_arg(fmt->args, Path*); + if(p == nil) + return fmtprint(fmt, "/"); + for(i = 1; i < p->nf; i++) + fmtprint(fmt, "p[%d] = %H", i, p->f[i]); + return 0; +}