From b21b9ba89cf66a8fac6f94efb79cfb425a2c4df2 Mon Sep 17 00:00:00 2001 From: aiju Date: Tue, 7 Aug 2012 17:57:04 +0200 Subject: [PATCH] added hjfs --- rc/bin/fstype | 4 + rc/bin/inst/bootsetup | 2 +- rc/bin/inst/configfs | 3 +- rc/bin/inst/mountcwfs | 2 + rc/bin/inst/mountfs | 2 + rc/bin/inst/mounthjfs | 70 +++ rc/bin/inst/prepdisk | 2 + rc/bin/inst/tzsetup | 0 sys/src/9/port/bootfs.proto | 1 + sys/src/cmd/hjfs/9p.c | 250 ++++++++++ sys/src/cmd/hjfs/auth.c | 508 +++++++++++++++++++ sys/src/cmd/hjfs/buf.c | 314 ++++++++++++ sys/src/cmd/hjfs/cons.c | 238 +++++++++ sys/src/cmd/hjfs/conv.c | 137 ++++++ sys/src/cmd/hjfs/dat.h | 228 +++++++++ sys/src/cmd/hjfs/dev.c | 93 ++++ sys/src/cmd/hjfs/dump.c | 169 +++++++ sys/src/cmd/hjfs/fns.h | 52 ++ sys/src/cmd/hjfs/fs1.c | 945 ++++++++++++++++++++++++++++++++++++ sys/src/cmd/hjfs/fs2.c | 742 ++++++++++++++++++++++++++++ sys/src/cmd/hjfs/main.c | 125 +++++ sys/src/cmd/hjfs/mkfile | 21 + 22 files changed, 3906 insertions(+), 2 deletions(-) create mode 100755 rc/bin/inst/mounthjfs mode change 100644 => 100755 rc/bin/inst/tzsetup create mode 100644 sys/src/cmd/hjfs/9p.c create mode 100644 sys/src/cmd/hjfs/auth.c create mode 100644 sys/src/cmd/hjfs/buf.c create mode 100644 sys/src/cmd/hjfs/cons.c create mode 100644 sys/src/cmd/hjfs/conv.c create mode 100644 sys/src/cmd/hjfs/dat.h create mode 100644 sys/src/cmd/hjfs/dev.c create mode 100644 sys/src/cmd/hjfs/dump.c create mode 100644 sys/src/cmd/hjfs/fns.h create mode 100644 sys/src/cmd/hjfs/fs1.c create mode 100644 sys/src/cmd/hjfs/fs2.c create mode 100644 sys/src/cmd/hjfs/main.c create mode 100644 sys/src/cmd/hjfs/mkfile diff --git a/rc/bin/fstype b/rc/bin/fstype index 5a0665c42..1820e8287 100755 --- a/rc/bin/fstype +++ b/rc/bin/fstype @@ -19,6 +19,10 @@ if(~ $"m 529ab12b){ echo paqfs exit } +if(~ $"m 011ce50d){ + echo hjfs + exit +} dd -if $1 -count 1 >[2]/dev/null | \ awk ' /^kfs/{fs["kfs"]++} diff --git a/rc/bin/inst/bootsetup b/rc/bin/inst/bootsetup index 190d69ec7..f5d5c4aa5 100755 --- a/rc/bin/inst/bootsetup +++ b/rc/bin/inst/bootsetup @@ -37,7 +37,7 @@ case go bootfile=9pcf @{ echo 'bootfile='^$bootfile - echo 'bootargs=local!'^$fs + echo 'bootargs=local!'^$fs^$fsflags if(~ $#nvram 1) echo 'nvram='^$nvram echo 'mouseport='^$mouseport diff --git a/rc/bin/inst/configfs b/rc/bin/inst/configfs index a0eea4838..81fc37a3a 100755 --- a/rc/bin/inst/configfs +++ b/rc/bin/inst/configfs @@ -14,8 +14,9 @@ case go echo 'You can install the following types of file systems:' echo echo ' cwfs64x the cached-worm file server' + echo ' hjfs the new 9front file server (experimental!)' echo - prompt -d cwfs64x 'File system' cwfs64x + prompt -d cwfs64x 'File system' cwfs64x hjfs fstype=$rd export fstype } diff --git a/rc/bin/inst/mountcwfs b/rc/bin/inst/mountcwfs index b92abf9e2..2af0e7773 100755 --- a/rc/bin/inst/mountcwfs +++ b/rc/bin/inst/mountcwfs @@ -22,6 +22,8 @@ case go prompt $default 'Cwfs cache partition' $files fs=$rd export fs + fsflags= + export fsflags files=(`{ls /dev/sd*/fsworm* /dev/fs/fsworm* >[2]/dev/null}) if(! ~ $#files 0) diff --git a/rc/bin/inst/mountfs b/rc/bin/inst/mountfs index 4f73ba578..10d41bde7 100755 --- a/rc/bin/inst/mountfs +++ b/rc/bin/inst/mountfs @@ -6,6 +6,8 @@ switch($fstype){ case cwfs cwfs64 cwfs64x exec mountcwfs $* +case hjfs + exec mounthjfs $* case * mountfs=notdone export mountfs diff --git a/rc/bin/inst/mounthjfs b/rc/bin/inst/mounthjfs new file mode 100755 index 000000000..6be5b4e6f --- /dev/null +++ b/rc/bin/inst/mounthjfs @@ -0,0 +1,70 @@ +#!/bin/rc + +# desc: choose and mount file system partition +# prereq: systype + +service=hjfs + +switch($1){ +case go + echo + echo The please choose your $fstype partition + echo + + files=(`{ls /dev/sd*/fs* >[2]/dev/null}) + if(! ~ $#files 0) + ls -l $files + echo + if(~ $#files 1) + default=(-d $files) + if not + default=() + prompt $default 'Hjfs partition' $files + fs=$rd + export fs + + mem=`{awk ' $2 == "pagesize" { p = $1 } $2 == "user" { split($1, a, "/"); print int((a[2] * p / 4 + 1048575) / 1048576) } ' '#c'/swap} + prompt -d $mem 'Size of RAM filesystem cache (MB)?' + fsflags=(-m $rd) + export fsflags + + log Starting $fstype file server for $fs + unmount /n/newfs >[2]/dev/null + echo halt >>/srv/$service.cmd >[2]/dev/null + rm -f /srv/$service /srv/$service.cmd + + hjfs -n $service $fsflags -Srf $fs + + log Configuring $fstype file server for $fs + { + echo echo on + echo create /dist sys sys 775 d + echo create /usr sys sys 775 d + echo newuser $user + echo newuser adm +$user + echo newuser sys +$user + echo newuser upas +$user + echo echo off + sleep 2 + } >>/srv/$service.cmd + + log Mounting $fstype file server for $fs + while(! logprog mount -c /srv/$service /n/newfs) + sleep 2 + if(! ~ $fsother ''){ + log Mounting $fstype file server for $fsother + logprog mount -c /srv/$service /n/other other + } + +case checkready checkdone + if(! ~ $fstype '' && ~ $#fs 1 && test -f $fs){ + if(test -f /srv/$service && test -d /n/newfs/dist){ + mountfs=done + export mountfs + exit + } + } + mountfs=ready + export mountfs + exit +} diff --git a/rc/bin/inst/prepdisk b/rc/bin/inst/prepdisk index 359b93621..5427fc61e 100755 --- a/rc/bin/inst/prepdisk +++ b/rc/bin/inst/prepdisk @@ -7,6 +7,8 @@ fn autotype { switch($fstype){ case cwfs cwfs64 cwfs64x echo -a 9fat -a nvram -a fscache -a fsworm -a other + case hjfs + echo -a 9fat -a nvram -a fs } } diff --git a/rc/bin/inst/tzsetup b/rc/bin/inst/tzsetup old mode 100644 new mode 100755 diff --git a/sys/src/9/port/bootfs.proto b/sys/src/9/port/bootfs.proto index af129c6f8..a7a767543 100644 --- a/sys/src/9/port/bootfs.proto +++ b/sys/src/9/port/bootfs.proto @@ -24,6 +24,7 @@ $objtype mntgen mount mv + hjfs rc rm sed diff --git a/sys/src/cmd/hjfs/9p.c b/sys/src/cmd/hjfs/9p.c new file mode 100644 index 000000000..fb3c3a9fd --- /dev/null +++ b/sys/src/cmd/hjfs/9p.c @@ -0,0 +1,250 @@ +#include +#include +#include +#include +#include <9p.h> +#include "dat.h" +#include "fns.h" + +extern Fs *fsmain; + +static void +tauth(Req *req) +{ + if((fsmain->flags & FSNOAUTH) != 0) + respond(req, "no authentication required"); + else + auth9p(req); +} + +static void +tattach(Req *req) +{ + Chan *ch; + int flags; + short uid; + + if((fsmain->flags & FSNOAUTH) == 0 && authattach(req) < 0) + return; + if(name2uid(fsmain, req->ifcall.uname, &uid) <= 0){ + respond(req, "no such user"); + return; + } + if(req->ifcall.aname == nil || *req->ifcall.aname == 0) + flags = 0; + else if(strcmp(req->ifcall.aname, "dump") == 0) + flags = CHFDUMP|CHFRO; + else{ + respond(req, Einval); + return; + } + ch = chanattach(fsmain, flags); + ch->uid = uid; + req->fid->aux = ch; + req->fid->qid = ch->loc->Qid; + req->ofcall.qid = ch->loc->Qid; + respond(req, nil); +} + +static void +tqueue(Req *req) +{ + Chan *ch; + + if((req->fid->qid.type & QTAUTH) != 0){ + switch(req->ifcall.type){ + case Tread: + authread(req); + return; + case Twrite: + authwrite(req); + return; + default: + respond(req, Einval); + return; + } + } + ch = req->fid->aux; + if(ch == nil){ + respond(req, "operation on closed fid"); + return; + } + qlock(&chanqu); + req->aux = nil; + if(ch->freq == nil) + ch->freq = req; + if(ch->lreq != nil) + ((Req *) ch->lreq)->aux = req; + ch->lreq = req; + if(ch->qnext == nil){ + ch->qnext = &readych; + ch->qprev = ch->qnext->qprev; + ch->qnext->qprev = ch; + ch->qprev->qnext = ch; + } + if(req->ifcall.type == Tremove) + req->fid->aux = nil; + rwakeup(&chanre); + qunlock(&chanqu); +} + +static void +tdestroyfid(Fid *fid) +{ + Chan *ch; + + if((fid->qid.type & QTAUTH) != 0){ + authdestroy(fid); + return; + } + qlock(&chanqu); + ch = fid->aux; + fid->aux = nil; + qunlock(&chanqu); + if(ch != nil) + chanclunk(ch); +} + +static void +tend(Srv *) +{ + shutdown(); +} + +static Srv mysrv = { + .auth = tauth, + .attach = tattach, + .walk = tqueue, + .open = tqueue, + .create = tqueue, + .read = tqueue, + .write = tqueue, + .stat = tqueue, + .wstat = tqueue, + .remove = tqueue, + .destroyfid = tdestroyfid, + .end = tend, +}; + +void +start9p(char *service, int stdio) +{ + if(stdio){ + mysrv.infd = 1; + mysrv.outfd = 1; + srv(&mysrv); + }else + threadpostmountsrv(&mysrv, service, nil, 0); +} + +static char * +tclone(Fid *old, Fid *new, void *) +{ + Chan *ch; + + ch = old->aux; + if(ch->open != 0) + return "trying to clone an open fid"; + new->aux = chanclone(ch); + return nil; +} + +static char * +twalk(Fid *fid, char *name, void *) +{ + Chan *ch; + char buf[ERRMAX]; + + ch = fid->aux; + if(chanwalk(ch, name) < 0){ + rerrstr(buf, ERRMAX); + return strdup(buf); + } + fid->qid = ch->loc->Qid; + return nil; +} + +static void +workerproc(void *) +{ + Chan *ch; + Req *req; + int rc; + Fcall *i, *o; + + qlock(&chanqu); + for(;;){ + while(readych.qnext == &readych) + rsleep(&chanre); + ch = readych.qnext; + ch->qnext->qprev = ch->qprev; + ch->qprev->qnext = ch->qnext; + ch->qprev = nil; + ch->qnext = nil; + while(ch != nil && ch->freq != nil){ + req = ch->freq; + ch->freq = req->aux; + if(ch->lreq == req) + ch->lreq = nil; + req->aux = nil; + qunlock(&chanqu); + i = &req->ifcall; + o = &req->ofcall; + switch(req->ifcall.type){ + case Twalk: + walkandclone(req, twalk, tclone, nil); + goto noret; + case Topen: + rc = chanopen(ch, i->mode); + break; + case Tcreate: + rc = chancreat(ch, i->name, i->perm, i->mode); + if(rc >= 0){ + o->qid = ch->loc->Qid; + req->fid->qid = o->qid; + } + break; + case Tread: + rc = o->count = chanread(ch, o->data, i->count, i->offset); + break; + case Twrite: + rc = o->count = chanwrite(ch, i->data, i->count, i->offset); + break; + case Tremove: + rc = chanremove(ch); + req->fid->aux = ch = nil; + break; + case Tstat: + rc = chanstat(ch, &req->d); + break; + case Twstat: + rc = chanwstat(ch, &req->d); + break; + default: + werrstr(Einval); + rc = -1; + } + if(rc < 0) + responderror(req); + else + respond(req, nil); + noret: + qlock(&chanqu); + } + } +} + +QLock chanqu; +Chan readych; +Rendez chanre; + +void +workerinit(void) +{ + int i; + + readych.qnext = readych.qprev = &readych; + chanre.l = &chanqu; + for(i = 0; i < NWORKERS; i++) + threadcreate(workerproc, nil, mainstacksize); +} diff --git a/sys/src/cmd/hjfs/auth.c b/sys/src/cmd/hjfs/auth.c new file mode 100644 index 000000000..efd026066 --- /dev/null +++ b/sys/src/cmd/hjfs/auth.c @@ -0,0 +1,508 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +typedef struct User User; +typedef struct PUser PUser; + +enum { USERMAX = 64 }; + +struct User { + short uid; + char name[USERMAX]; + short lead; + int nmemb; + short *memb; +}; + +struct PUser { + short uid; + char name[USERMAX]; + char lead[USERMAX]; + int nmemb; + char (*memb)[USERMAX]; +}; + +User udef[] = { + {-1, "adm", -1, 0, nil}, + {0, "none", -1, 0, nil}, + {1, "tor", 1, 0, nil}, + {2, "glenda", 2, 0, nil}, + {10000, "sys", NOUID, 0, nil}, + {10001, "map", 10001, 0, nil}, + {10002, "doc", NOUID, 0, nil}, + {10003, "upas", 10003, 0, nil}, + {10004, "font", NOUID, 0, nil}, +}; + +static int +validuser(char *n) +{ + char *p; + + if(*n == 0) + return 0; + for(p = n; *p != 0; p++) + if((uchar) *p < ' ' || strchr("?=+-/:", *p) != nil) + return 0; + return n - p < USERMAX; +} + +static void +usersparseline(char *l, PUser **u, int *nu) +{ + PUser v; + char *f[5], *r, *s; + int c; + + if(*l == 0 || *l == '#') + return; + c = getfields(l, f, 5, 0, ":"); + if(c < 4) + return; + v.uid = strtol(f[0], &r, 10); + if(*r != 0) + return; + if(!validuser(f[1]) || *f[2] != 0 && !validuser(f[2])) + return; + strcpy(v.name, f[1]); + strcpy(v.lead, f[2]); + v.memb = nil; + v.nmemb = 0; + r = f[3]; + while(r != nil && *r != 0){ + s = strchr(r, ','); + if(s != nil) + *s = 0; + if(!validuser(r)){ + free(v.memb); + return; + } + v.memb = realloc(v.memb, (v.nmemb + 1) * USERMAX); + strcpy(v.memb[v.nmemb++], r); + if(s == nil) + r = nil; + else + r = s + 1; + } + *u = realloc(*u, (*nu + 1) * sizeof(PUser)); + memcpy(&(*u)[(*nu)++], &v, sizeof(PUser)); +} + +static int +puserlook(PUser *u, int nu, char *name) +{ + PUser *v; + + if(*name == 0) + return NOUID; + for(v = u; v < u + nu; v++) + if(strcmp(v->name, name) == 0) + return v->uid; + return NOUID; +} + +static int +uidcomp(void *a, void *b) +{ + short *aa, *bb; + + aa = a; + bb = b; + return *aa - *bb; +} + +static int +usercomp(void *a, void *b) +{ + User *aa, *bb; + + aa = a; + bb = b; + return aa->uid - bb->uid; +} + +int +usersload(Fs *fs, Chan *ch) +{ + char *buf, *p, *q; + int bufl, i, j, rc, nu; + PUser *u; + User *v; + + buf = nil; + bufl = 0; + u = nil; + v = nil; + nu = 0; + for(;;){ + if((bufl & 1023) == 0) + buf = realloc(buf, bufl + 1024); + rc = chanread(ch, buf + bufl, 1024, bufl); + if(rc < 0) + goto err; + if(rc == 0) + break; + bufl += rc; + } + if(buf == nil) + goto done; + buf[bufl] = 0; + for(p = buf; q = strchr(p, '\n'); p = q + 1){ + *q = 0; + usersparseline(p, &u, &nu); + } + usersparseline(p, &u, &nu); + free(buf); + if(nu == 0) + goto done; + v = emalloc(sizeof(User) * nu); + for(i = 0; i < nu; i++){ + v[i].uid = u[i].uid; + strcpy(v[i].name, u[i].name); + v[i].lead = puserlook(u, nu, u[i].lead); + v[i].nmemb = u[i].nmemb; + v[i].memb = emalloc(sizeof(short) * v[i].nmemb); + for(j = 0; j < v[i].nmemb; j++) + v[i].memb[j] = puserlook(u, nu, u[i].memb[j]); + qsort(v[i].memb, v[i].nmemb, sizeof(ushort), uidcomp); + } + qsort(v, nu, sizeof(User), usercomp); +done: + wlock(&fs->udatal); + if(fs->udata != nil){ + for(i = 0; i < fs->nudata; i++) + free(((User *)fs->udata)[i].memb); + free(fs->udata); + } + fs->udata = v; + fs->nudata = nu; + wunlock(&fs->udatal); + return 0; +err: + free(buf); + return -1; +} + +int +userssave(Fs *fs, Chan *ch) +{ + User *u, *v; + int nu, i; + char buf[512], *p, *e; + uvlong off; + + rlock(&fs->udatal); + u = fs->udata; + if(u == nil){ + u = udef; + nu = nelem(udef); + }else + nu = fs->nudata; + off = 0; + for(v = u; v < u + nu; v++){ + p = buf; + e = buf + sizeof(buf); + p = seprint(p, e, "%d:%s:", v->uid, v->name); + if(v->lead != NOUID) + p = strecpy(p, e, uid2name(fs, v->lead)); + if(p < e) + *p++ = ':'; + for(i = 0; i < v->nmemb; i++){ + if(v->memb[i] == NOUID) + continue; + if(p < e && i > 0) + *p++ = ','; + p = strecpy(p, e, uid2name(fs, v->memb[i])); + } + *p++ = '\n'; + if(ch == nil) + write(2, buf, p - buf); + else if(chanwrite(ch, buf, p - buf, off) < p - buf) + goto err; + off += p - buf; + } + runlock(&fs->udatal); + return 0; +err: + runlock(&fs->udatal); + return -1; +} + +static User * +lookupuid(Fs *fs, short uid) +{ + User *u; + int i, j, k; + + u = fs->udata; + i = 0; + j = fs->nudata; + if(u == nil){ + u = udef; + j = nelem(udef); + } + if(j == 0) + return nil; + while(i < j){ + k = (i + j) / 2; + if(u[k].uid < uid) + i = k + 1; + else + j = k; + } + if(u[i].uid == uid) + return &u[i]; + return nil; +} + +int +ingroup(Fs *fs, short uid, short gid, int leader) +{ + User *g; + int i, j, k; + + if(uid == gid) + return 1; + rlock(&fs->udatal); + g = lookupuid(fs, gid); + if(g == nil) + goto nope; + if(g->lead == uid) + goto yes; + if(leader && g->lead != NOUID) + goto nope; + if(g->nmemb == 0) + goto nope; + i = 0; + j = g->nmemb; + while(i < j){ + k = (i + j) / 2; + if(g->memb[k] < uid) + i = k + 1; + else + j = k; + } + if(g->memb[i] == uid) + goto yes; +nope: + runlock(&fs->udatal); + return 0; +yes: + runlock(&fs->udatal); + return 1; +} + +int +permcheck(Fs *fs, Dentry *d, short uid, int mode) +{ + int perm; + + if((fs->flags & FSNOPERM) != 0) + return 1; + perm = d->mode & 0777; + if(d->uid == uid) + perm >>= 6; + else if(ingroup(fs, uid, d->gid, 0)) + perm >>= 3; + switch(mode & 3){ + case OREAD: + return (perm & 4) != 0; + case OWRITE: + return (perm & 2) != 0; + case OEXEC: + return (perm & 1) != 0; + case ORDWR: + return (perm & 5) == 5; + } + return 0; +} + +char * +uid2name(Fs *fs, short uid) +{ + User *u; + char *s; + + rlock(&fs->udatal); + u = lookupuid(fs, uid); + if(u == nil) + s = smprint("%d", uid); + else + s = strdup(u->name); + runlock(&fs->udatal); + return s; +} + +int +name2uid(Fs *fs, char *name, short *uid) +{ + char *r; + User *u, *v; + + *uid = strtol(name, &r, 10); + if(*r == 0) + return 1; + rlock(&fs->udatal); + u = fs->udata; + v = u + fs->nudata; + if(u == nil){ + u = udef; + v = udef + nelem(udef); + } + for(; u < v; u++) + if(strcmp(u->name, name) == 0){ + *uid = u->uid; + runlock(&fs->udatal); + return 1; + } + runlock(&fs->udatal); + werrstr(Einval); + return -1; +} + +static void +createuserdir(Fs *fs, char *name, short uid) +{ + Chan *ch; + Buf *b, *c; + Dentry *d; + FLoc f; + + ch = chanattach(fs, 0); + if(ch == nil) + return; + ch->uid = -1; + if(chanwalk(ch, "usr") <= 0 || (ch->loc->type & QTDIR) == 0){ + direrr: + chanclunk(ch); + return; + } + chbegin(ch); + if(willmodify(ch->fs, ch->loc, 0) < 0){ + direrr1: + chend(ch); + goto direrr; + } + b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0); + if(b == nil) + goto direrr1; + if(newentry(ch->fs, ch->loc, b, name, &f) <= 0){ + direrr2: + putbuf(b); + goto direrr1; + } + modified(ch, &b->de[ch->loc->deind]); + c = getbuf(ch->fs->d, f.blk, TDENTRY, 0); + if(c == nil) + goto direrr2; + d = &c->de[f.deind]; + memset(d, 0, sizeof(Dentry)); + strcpy(d->name, name); + d->uid = uid; + d->muid = uid; + d->gid = uid; + d->mode = DALLOC | 0775; + if(newqid(fs, &d->path) < 0){ + direrr3: + putbuf(c); + goto direrr2; + } + d->type = QTDIR; + d->atime = time(0); + d->mtime = d->atime; + c->op |= BDELWRI; + goto direrr3; +} + +int +cmdnewuser(int argc, char **argv) +{ + short uid, gid; + User *u, *v; + Fs *fs; + int resort, createdir, i, j; + extern Fs *fsmain; + + if(argc < 2) + return -9001; + if(!validuser(argv[1])){ + werrstr(Einval); + return -1; + } + fs = fsmain; + resort = 0; + createdir = 0; + wlock(&fs->udatal); + if(fs->udata == nil){ + wunlock(&fs->udatal); + werrstr("newuser: no user database"); + return -1; + } + uid = 0; + gid = 10000; + for(u = fs->udata; u < fs->udata + fs->nudata; u++){ + if(strcmp(u->name, argv[1]) == 0) + goto found; + if(u->uid == uid) + uid++; + if(u->uid == gid) + gid++; + } + resort = 1; + fs->udata = realloc(fs->udata, sizeof(User) * (fs->nudata + 1)); + u = fs->udata + fs->nudata++; + strcpy(u->name, argv[1]); + u->nmemb = 0; + u->memb = nil; + u->uid = gid; + u->lead = NOUID; + if(argc == 2 || strcmp(argv[2], ":") != 0){ + u->lead = u->uid = uid; + createdir = 1; + } +found: + for(i = 2; i < argc; i++){ + if(strcmp(argv[i], ":") == 0) + continue; + if(*argv[i] != '+' && *argv[i] != '-' && *argv[i] != '='){ + if(!validuser(argv[i])) + goto erropt; + strcpy(u->name, argv[i]); + continue; + } + for(v = fs->udata; v < fs->udata + fs->nudata; v++) + if(strcmp(v->name, argv[i] + 1) == 0) + break; + if(v == fs->udata + fs->nudata) + goto erropt; + if(*argv[i] == '='){ + u->lead = v->uid; + continue; + } + for(j = 0; j < u->nmemb && u->memb[j] < v->uid; j++) + ; + if(*argv[i] == '-'){ + if(u->memb[j] != v->uid) + goto erropt; + memmove(&u->memb[j], &u->memb[j + 1], sizeof(short) * (u->nmemb - j - 1)); + u->memb = realloc(u->memb, sizeof(short) * --u->nmemb); + }else{ + u->memb = realloc(u->memb, sizeof(short) * ++u->nmemb); + memmove(&u->memb[j + 1], &u->memb[j], sizeof(short) * (u->nmemb - j - 1)); + u->memb[j] = v->uid; + } + continue; + erropt: + dprint("hjfs: newuser: ignoring erroneous option %s\n", argv[i]); + } + if(resort) + qsort(fs->udata, fs->nudata, sizeof(User), usercomp); + wunlock(&fs->udatal); + writeusers(fs); + if(createdir) + createuserdir(fs, argv[2], uid); + return 1; +} diff --git a/sys/src/cmd/hjfs/buf.c b/sys/src/cmd/hjfs/buf.c new file mode 100644 index 000000000..23f533520 --- /dev/null +++ b/sys/src/cmd/hjfs/buf.c @@ -0,0 +1,314 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +Channel *getb, *putb, *syncb; +Buf bfree; +BufReq *freereq, *freereqlast; + +static void +markbusy(Buf *b) +{ + b->busy = 1; + if(b->fnext != nil){ + b->fnext->fprev = b->fprev; + b->fprev->fnext = b->fnext; + b->fnext = b->fprev = nil; + } +} + +static void +markfree(Buf *b) +{ + b->busy = 0; + b->fnext = &bfree; + b->fprev = bfree.fprev; + b->fnext->fprev = b; + b->fprev->fnext = b; +} + +static void +changedev(Buf *b, Dev *d, uvlong off) +{ + if(b->dnext != nil){ + b->dnext->dprev = b->dprev; + b->dprev->dnext = b->dnext; + b->dprev = nil; + b->dnext = nil; + } + b->off = off; + b->d = d; + if(d != nil){ + b->dnext = &d->buf[b->off & BUFHASH]; + b->dprev = b->dnext->dprev; + b->dnext->dprev = b; + b->dprev->dnext = b; + } +} + +static void +delayreq(BufReq req, BufReq **first, BufReq **last) +{ + BufReq *r; + + r = emalloc(sizeof(*r)); + memcpy(r, &req, sizeof(*r)); + r->next = nil; + if(*first == nil) + *first = *last = r; + else{ + (*last)->next = r; + *last = r; + } +} + +static void +work(Dev *d, Buf *b) +{ + qlock(&d->workl); + b->wnext = &d->work; + b->wprev = b->wnext->wprev; + b->wnext->wprev = b; + b->wprev->wnext = b; + rwakeup(&d->workr); + qunlock(&d->workl); +} + +static void +givebuf(BufReq req, Buf *b) +{ + Buf *c, *l; + + markbusy(b); + if(req.d == b->d && req.off == b->off){ + send(req.resp, &b); + return; + } + if(b->op & BDELWRI){ + b->op &= ~BDELWRI; + b->op |= BWRITE; + delayreq(req, &b->next, &b->last); + b->resp = putb; + work(b->d, b); + return; + } + l = &req.d->buf[req.off & BUFHASH]; + for(c = l->dnext; c != l; c = c->dnext) + if(c->off == req.off) + abort(); + changedev(b, req.d, req.off); + b->op &= ~(BWRITE|BDELWRI|BWRIM); + if(req.nodata) + send(req.resp, &b); + else{ + b->resp = req.resp; + work(b->d, b); + } +} + +static void +undelayreq(Buf *b, BufReq **first, BufReq **last) +{ + BufReq *r; + + r = *first; + *first = r->next; + if(*last == r) + *last = nil; + givebuf(*r, b); + free(r); +} + +static void +handleget(BufReq req) +{ + Buf *b, *l; + Dev *d; + + d = req.d; + l = &d->buf[req.off & BUFHASH]; + for(b = l->dnext; b != l; b = b->dnext) + if(b->off == req.off){ + if(b->busy){ + delayreq(req, &b->next, &b->last); + return; + } + givebuf(req, b); + return; + } + if(bfree.fnext == &bfree){ + delayreq(req, &freereq, &freereqlast); + return; + } + b = bfree.fnext; + givebuf(req, b); +} + +static void +handleput(Buf *b) +{ + if(b->op & BWRIM){ + b->op &= ~(BWRIM | BDELWRI); + b->op |= BWRITE; + b->resp = putb; + work(b->d, b); + return; + } + if(b->error != nil){ + b->error = nil; + b->op &= ~BDELWRI; + changedev(b, nil, -1); + } + b->op &= ~BWRITE; + markfree(b); + if(b->next != nil) + undelayreq(b, &b->next, &b->last); + else if(freereq != nil) + undelayreq(b, &freereq, &freereqlast); +} + +static void +handlesync(Channel *resp) +{ + Buf *b, *c; + + for(b = bfree.fnext; b != &bfree; b = c){ + c = b->fnext; + if(b->d != nil && b->op & BDELWRI){ + markbusy(b); + b->resp = putb; + b->op &= ~BDELWRI; + b->op |= BWRITE; + work(b->d, b); + } + } + if(resp != nil) + sendp(resp, nil); +} + +static void +bufproc(void *) +{ + BufReq req; + Buf *buf; + Channel *r; + Alt a[] = {{getb, &req, CHANRCV}, {putb, &buf, CHANRCV}, {syncb, &r, CHANRCV}, {nil, nil, CHANEND}}; + + workerinit(); + for(;;) + switch(alt(a)){ + case 0: + handleget(req); + break; + case 1: + handleput(buf); + break; + case 2: + handlesync(r); + break; + case -1: + sysfatal("alt: %r"); + } +} + +static char * +typenames[] = { + [TRAW] "raw", + [TSUPERBLOCK] "superblock", + [TDENTRY] "dentry", + [TINDIR] "indir", + [TREF] "ref", + nil +}; + +static int +Tfmt(Fmt *f) +{ + int t; + + t = va_arg(f->args, uint); + if(t >= nelem(typenames) || typenames[t] == nil) + return fmtprint(f, "??? (%d)", t); + return fmtstrcpy(f, typenames[t]); +} + +void +bufinit(int nbuf) +{ + Buf *b; + + fmtinstall('T', Tfmt); + b = emalloc(sizeof(*b) * nbuf); + bfree.fnext = bfree.fprev = &bfree; + while(nbuf--) + markfree(b++); + getb = chancreate(sizeof(BufReq), 0); + putb = chancreate(sizeof(Buf *), 32); + syncb = chancreate(sizeof(ulong), 0); + proccreate(bufproc, nil, mainstacksize); +} + +Buf * +getbuf(Dev *d, uvlong off, int type, int nodata) +{ + ThrData *th; + BufReq req; + Buf *b; + + if(off >= d->size) + abort(); + th = getthrdata(); + req.d = d; + req.off = off; + req.resp = th->resp; + req.nodata = nodata; + send(getb, &req); + recv(th->resp, &b); + if(nodata) + b->type = type; + if(b->type != type && type != -1){ + dprint("hjfs: type mismatch, dev %s, block %lld, got %T, want %T, caller %#p\n", d->name, off, b->type, type, getcallerpc(&d)); + werrstr("phase error -- type mismatch"); + putbuf(b); + return nil; + } + if(b->error != nil){ + werrstr("%s", b->error); + putbuf(b); + return nil; + } + b->callerpc = getcallerpc(&d); + return b; +} + +void +putbuf(Buf *b) +{ + send(putb, &b); +} + +void +sync(int wait) +{ + Channel *r; + Dev *d; + Buf b; + + r = nil; + if(wait) + r = getthrdata()->resp; + sendp(syncb, r); + memset(&b, 0, sizeof(Buf)); + if(wait){ + recvp(r); + for(d = devs; d != nil; d = d->next){ + b.d = nil; + b.resp = r; + b.busy = 1; + work(d, &b); + recvp(r); + } + } +} diff --git a/sys/src/cmd/hjfs/cons.c b/sys/src/cmd/hjfs/cons.c new file mode 100644 index 000000000..db76452b2 --- /dev/null +++ b/sys/src/cmd/hjfs/cons.c @@ -0,0 +1,238 @@ +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +enum { MAXARGS = 16 }; + +typedef struct Cmd Cmd; +static int echo; +extern Fs *fsmain; + +struct Cmd { + char *name; + int args; + int (*f)(int, char **); +}; + +static int +walkpath(Chan *ch, char *path, char **cr) +{ + char buf[NAMELEN], *p, *fp; + + buf[NAMELEN - 1] = 0; + fp = path; + if(*path != '/'){ + noent: + werrstr("%s: %s", fp, Enoent); + return -1; + } + path++; + for(;;){ + p = strchr(path, '/'); + if(p == nil){ + if(cr != nil){ + if(*path == 0){ + werrstr("%s: trailing slash", fp); + return -1; + } + *cr = path; + break; + } + p = path + strlen(path); + } + if(*path == '/'){ + path++; + continue; + } + if(*path == 0) + break; + if(p - path >= NAMELEN) + goto noent; + memcpy(buf, path, p - path); + if(chanwalk(ch, buf) <= 0){ + werrstr("%s: %r", fp); + return -1; + } + if(*p == 0) + break; + path = p + 1; + } + return 1; +} + +int +cmdhalt(int, char **) +{ + shutdown(); + return 0; +} + +int +cmddump(int, char **) +{ + fsdump(fsmain); + dprint("hjfs: dumped\n"); + return 0; +} + +int +cmdallow(int, char **) +{ + fsmain->flags |= FSNOPERM | FSCHOWN; + dprint("hjfs: allow\n"); + return 0; +} + +int +cmdchatty(int, char **) +{ + extern int chatty9p; + + chatty9p = !chatty9p; + return 0; +} + +int +cmddisallow(int, char **) +{ + fsmain->flags &= ~(FSNOPERM | FSCHOWN); + dprint("hjfs: disallow\n"); + return 0; +} + +int +cmdnoauth(int, char **) +{ + fsmain->flags ^= FSNOAUTH; + if((fsmain->flags & FSNOAUTH) != 0) + dprint("hjfs: auth enabled\n"); + else + dprint("hjfs: auth disabled\n"); + return 1; +} + +int +cmdcreate(int argc, char **argv) +{ + Chan *ch; + char *n; + short uid, gid; + ulong perm; + Dir di; + + if(argc != 5 && argc != 6) + return -9001; + perm = strtol(argv[4], &n, 8) & 0777; + if(*n != 0) + return -9001; + if(argc == 6) + for(n = argv[5]; *n != 0; n++) + switch(*n){ + case 'l': perm |= DMEXCL; break; + case 'd': perm |= DMDIR; break; + case 'a': perm |= DMAPPEND; break; + default: return -9001; + } + if(name2uid(fsmain, argv[2], &uid) < 0) + return -1; + if(name2uid(fsmain, argv[3], &gid) < 0) + return -1; + ch = chanattach(fsmain, 0); + if(ch == nil) + return -1; + ch->uid = uid; + if(walkpath(ch, argv[1], &n) < 0){ + chanclunk(ch); + return -1; + } + if(chancreat(ch, n, perm, OREAD) < 0){ + chanclunk(ch); + return -1; + } + nulldir(&di); + di.gid = argv[3]; + chanwstat(ch, &di); + chanclunk(ch); + return 1; +} + +int +cmdecho(int, char **argv) +{ + echo = strcmp(argv[1], "on") == 0; + return 1; +} + +extern int cmdnewuser(int, char **); + +Cmd cmds[] = { + {"allow", 1, cmdallow}, + {"noauth", 1, cmdnoauth}, + {"chatty", 1, cmdchatty}, + {"create", 0, cmdcreate}, + {"disallow", 1, cmddisallow}, + {"dump", 1, cmddump}, + {"halt", 1, cmdhalt}, + {"newuser", 0, cmdnewuser}, + {"echo", 2, cmdecho}, +}; + + +static void +consproc(void *v) +{ + Biobuf *in; + Cmd *c; + char *s; + char *args[MAXARGS]; + int rc; + + in = (Biobuf *) v; + for(;;){ + s = Brdstr(in, '\n', 1); + if(s == nil) + continue; + if(echo) + dprint("hjfs: >%s\n", s); + rc = tokenize(s, args, MAXARGS); + if(rc == 0) + goto syntax; + for(c = cmds; c < cmds + nelem(cmds); c++) + if(strcmp(c->name, args[0]) == 0){ + if(c->args != 0 && c->args != rc) + goto syntax; + if(c->f != nil){ + rc = c->f(rc, args); + if(rc == -9001) + goto syntax; + if(rc < 0) + dprint("hjfs: %r\n"); + goto done; + } + } + syntax: + dprint("hjfs: syntax error\n"); + done: + free(s); + } +} + +void +initcons(char *service) +{ + int fd, pfd[2]; + static Biobuf bio; + char buf[512]; + + snprint(buf, sizeof(buf), "/srv/%s.cmd", service); + fd = create(buf, OWRITE|ORCLOSE, 0600); + if(fd < 0) + return; + pipe(pfd); + fprint(fd, "%d", pfd[1]); + Binit(&bio, pfd[0], OREAD); + proccreate(consproc, &bio, mainstacksize); +} diff --git a/sys/src/cmd/hjfs/conv.c b/sys/src/cmd/hjfs/conv.c new file mode 100644 index 000000000..21333e222 --- /dev/null +++ b/sys/src/cmd/hjfs/conv.c @@ -0,0 +1,137 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +#define GET8(x) {x = *p++;} +#define GET16(x) {x = *p++; x |= *p++ << 8;} +#define GET24(x) {x = *p++; x |= *p++ << 8; x |= *p++ << 16;} +#define GET32(x) {x = *p++; x |= *p++ << 8; x |= *p++ << 16; x |= *p++ << 24;} +#define GET64(x) \ + {x = (uvlong) *p++; \ + x |= (uvlong) *p++ << 8; \ + x |= (uvlong) *p++ << 16; \ + x |= (uvlong) *p++ << 24; \ + x |= (uvlong) *p++ << 32; \ + x |= (uvlong) *p++ << 40; \ + x |= (uvlong) *p++ << 48; \ + x |= (uvlong) *p++ << 56;} +#define GETS(x, n) {memcpy(x, p, n); p += n;} + +#define PUT8(x) {*p++ = x;} +#define PUT16(x) {*p++ = x; *p++ = x >> 8;} +#define PUT24(x) {*p++ = x; *p++ = x >> 8; *p++ = x >> 16;} +#define PUT32(x) {*p++ = x; *p++ = x >> 8; *p++ = x >> 16; *p++ = x >> 24;} +#define PUT64(x) \ + {*p++ = x; \ + *p++ = x >> 8; \ + *p++ = x >> 16; \ + *p++ = x >> 24; \ + *p++ = x >> 32; \ + *p++ = x >> 40; \ + *p++ = x >> 48; \ + *p++ = x >> 56;} +#define PUTS(x, n) {memcpy(p, x, n); p += n;} + +void +unpack(Buf *b, uchar *p) +{ + Dentry *d; + int i; + + switch(b->type = *p++){ + default: + memcpy(b->data, p, RBLOCK); + break; + case TSUPERBLOCK: + GET32(b->sb.magic); + GET64(b->sb.size); + GET64(b->sb.fstart); + GET64(b->sb.fend); + GET64(b->sb.root); + GET64(b->sb.qidpath); + break; + case TDENTRY: + for(d = b->de; d < b->de + nelem(b->de); d++){ + GETS(d->name, NAMELEN); + GET16(d->uid); + GET16(d->muid); + GET16(d->gid); + GET16(d->mode); + GET64(d->path); + GET32(d->vers); + GET8(d->type); + GET64(d->size); + for(i = 0; i < NDIRECT; i++) + GET64(d->db[i]); + for(i = 0; i < NINDIRECT; i++) + GET64(d->ib[i]); + GET64(d->atime); + GET64(d->mtime); + } + break; + case TINDIR: + for(i = 0; i < OFFPERBLK; i++) + GET64(b->offs[i]); + break; + case TREF: + for(i = 0; i < REFPERBLK; i++) + GET24(b->refs[i]); + break; + } + USED(p); +} + +void +pack(Buf *b, uchar *p) +{ + Dentry *d; + int i; + + switch(*p++ = b->type){ + case TRAW: + memcpy(p, b->data, RBLOCK); + break; + + case TSUPERBLOCK: + PUT32(b->sb.magic); + PUT64(b->sb.size); + PUT64(b->sb.fstart); + PUT64(b->sb.fend); + PUT64(b->sb.root); + PUT64(b->sb.qidpath); + break; + case TDENTRY: + for(d = b->de; d < b->de + nelem(b->de); d++){ + PUTS(d->name, NAMELEN); + PUT16(d->uid); + PUT16(d->muid); + PUT16(d->gid); + PUT16(d->mode); + PUT64(d->path); + PUT32(d->vers); + PUT8(d->type); + PUT64(d->size); + for(i = 0; i < NDIRECT; i++) + PUT64(d->db[i]); + for(i = 0; i < NINDIRECT; i++) + PUT64(d->ib[i]); + PUT64(d->atime); + PUT64(d->mtime); + } + break; + case TINDIR: + for(i = 0; i < OFFPERBLK; i++) + PUT64(b->offs[i]); + break; + case TREF: + for(i = 0; i < REFPERBLK; i++) + PUT24(b->refs[i]); + break; + default: + abort(); + } + USED(p); +} + diff --git a/sys/src/cmd/hjfs/dat.h b/sys/src/cmd/hjfs/dat.h new file mode 100644 index 000000000..6201eb50a --- /dev/null +++ b/sys/src/cmd/hjfs/dat.h @@ -0,0 +1,228 @@ +enum { + /* affects on-disk structure */ + BLOCK = 4096, + RBLOCK = BLOCK - 1, + SUPERMAGIC = 0x6E0DE51C, + SUPERBLK = 0, + + NAMELEN = 256, + NDIRECT = 15, + NINDIRECT = 4, + + ROOTQID = 1, + DUMPROOTQID = 2, + + /* affects just run-time behaviour */ + SYNCINTERVAL = 10000, + FREELISTLEN = 256, + BUFHASHBITS = 8, + BUFHASH = (1< +#include +#include +#include "dat.h" +#include "fns.h" + +Dev *devs; + +void +devwork(void *v) +{ + Dev *d; + Buf *b; + Channel *r; + uchar buf[BLOCK]; + + d = v; + for(;;){ + qlock(&d->workl); + while(d->work.wnext == &d->work) + rsleep(&d->workr); + b = d->work.wnext; + b->wnext->wprev = b->wprev; + b->wprev->wnext = b->wnext; + b->wnext = b->wprev = nil; + qunlock(&d->workl); + if(b->d == nil) /* this is a sync request */ + goto reply; + if(b->off >= d->size){ + b->error = Einval; + goto reply; + } + seek(d->fd, b->off * BLOCK, 0); + b->error = nil; + if(b->op & BWRITE){ + memset(buf, 0, sizeof(buf)); + pack(b, buf); + if(write(d->fd, buf, BLOCK) < BLOCK){ + dprint("hjfs: write: %r\n"); + b->error = Eio; + } + }else{ + if(readn(d->fd, buf, BLOCK) < 0){ + dprint("hjfs: read: %r\n"); + b->error = Eio; + }else + unpack(b, buf); + } + reply: + r = b->resp; + b->resp = nil; + if(r != nil) + send(r, &b); + } +} + +Dev * +newdev(char *file) +{ + Dev *d, **e; + Dir *dir; + Buf *b; + + d = emalloc(sizeof(*d)); + d->fd = open(file, ORDWR); + if(d->fd < 0){ + free(d); + return nil; + } + dir = dirfstat(d->fd); + if(dir == nil){ + error: + close(d->fd); + free(d); + return nil; + } + d->size = dir->length / BLOCK; + free(dir); + if(d->size == 0){ + werrstr("device file too short"); + goto error; + } + d->name = strdup(file); + for(b = d->buf; b < d->buf + BUFHASH + 1; b++) + b->dnext = b->dprev = b; + d->workr.l = &d->workl; + d->work.wnext = d->work.wprev = &d->work; + proccreate(devwork, d, mainstacksize); + for(e = &devs; *e != nil; e = &(*e)->next) + ; + *e = d; + return d; +} diff --git a/sys/src/cmd/hjfs/dump.c b/sys/src/cmd/hjfs/dump.c new file mode 100644 index 000000000..06c6e2418 --- /dev/null +++ b/sys/src/cmd/hjfs/dump.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +int +copydentry(Fs *fs, FLoc *a, Loc *b, char *nname) +{ + Buf *ba, *bb, *bc; + Dentry *d; + int i, rc; + FLoc c; + + if(!namevalid(nname)){ + werrstr(Einval); + return -1; + } + ba = getbuf(fs->d, a->blk, TDENTRY, 0); + if(ba == nil) + return -1; + bb = getbuf(fs->d, b->blk, TDENTRY, 0); + if(bb == nil){ + putbuf(ba); + return -1; + } + rc = newentry(fs, b, bb, nname, &c); + if(rc < 0){ + err1: + putbuf(bb); + putbuf(ba); + return -1; + } + bc = getbuf(fs->d, c.blk, TDENTRY, 0); + if(bc == nil) + goto err1; + d = &bc->de[c.deind]; + memcpy(d, &ba->de[a->deind], sizeof(Dentry)); + strcpy(d->name, nname); + for(i = 0; i < NDIRECT; i++) + if(d->db[i] != 0) + chref(fs, d->db[i], 1); + for(i = 0; i < NINDIRECT; i++) + if(d->ib[i] != 0) + chref(fs, d->ib[i], 1); + bc->op |= BDELWRI; + putbuf(bc); + putbuf(bb); + putbuf(ba); + return 0; +} + +int +fsdump(Fs *fs) +{ + char buf[20], *p, *e; + int n, rc; + Tm *tm; + Chan *ch, *chh; + Buf *b; + + wlock(fs); + tm = localtime(time(0)); + snprint(buf, sizeof(buf), "%.4d", tm->year + 1900); + ch = chanattach(fs, CHFNOLOCK|CHFDUMP); + ch->uid = -1; + if(ch == nil){ + wunlock(fs); + return -1; + } + if(chanwalk(ch, buf) < 0){ + chh = chanclone(ch); + rc = chancreat(chh, buf, DMDIR|0555, OREAD); + chanclunk(chh); + if(rc < 0) + goto err; + if(chanwalk(ch, buf) < 0) + goto err; + } + b = getbuf(fs->d, ch->loc->blk, TDENTRY, 0); + if(b == nil) + goto err; + for(n = 0; ; n++){ + e = buf + sizeof(buf); + p = seprint(buf, e, "%.2d%.2d", tm->mon + 1, tm->mday); + if(n > 0) + seprint(p, e, "%d", n); + rc = findentry(fs, ch->loc, b, buf, nil, 1); + if(rc < 0) + goto err; + if(rc == 0) + break; + } + putbuf(b); + rc = copydentry(fs, fs->rootloc, ch->loc, buf); + chanclunk(ch); + wunlock(fs); + return rc; +err: + chanclunk(ch); + wunlock(fs); + return -1; +} + +int +willmodify(Fs *fs, Loc *l, int nolock) +{ + Buf *p; + uvlong i, r; + Dentry *d; + int rc; + + if(!nolock){ +again: + runlock(fs); + wlock(fs); + } + rc = chref(fs, l->blk, 0); + if(rc < 0) + goto err; + if(rc == 0){ + dprint("hjfs: willmodify: block %lld has refcount 0\n", l->blk); + werrstr("phase error -- willmodify"); + goto err; + } + if(rc == 1) + goto done; + if(willmodify(fs, l->next, 1) < 0) + goto err; + p = getbuf(fs->d, l->next->blk, TDENTRY, 0); + if(p == nil) + goto err; + d = &p->de[l->next->deind]; + for(i = 0; i < d->size; i++){ + rc = getblk(fs, l->next, p, i, &r, GBREAD); + if(rc <= 0) + continue; + if(r == l->blk) + goto found; + } +phase: + werrstr("willmodify -- phase error"); + putbuf(p); + goto err; +found: + rc = getblk(fs, l->next, p, i, &r, GBWRITE); + if(rc < 0){ + putbuf(p); + goto err; + } + if(rc == 0) + goto phase; + putbuf(p); + l->blk = r; +done: + if(!nolock){ + wunlock(fs); + rlock(fs); + if(chref(fs, l->blk, 0) != 1) + goto again; + } + return 0; +err: + if(!nolock){ + wunlock(fs); + rlock(fs); + } + return -1; +} diff --git a/sys/src/cmd/hjfs/fns.h b/sys/src/cmd/hjfs/fns.h new file mode 100644 index 000000000..35e36fa06 --- /dev/null +++ b/sys/src/cmd/hjfs/fns.h @@ -0,0 +1,52 @@ +void* emalloc(int); +void bufinit(int); +Buf* getbuf(Dev *, uvlong, int, int); +void putbuf(Buf *); +void sync(int); +void pack(Buf *, uchar *); +void unpack(Buf *, uchar *); +Dev* newdev(char *); +ThrData* getthrdata(void); +Fs* initfs(Dev *, int, int); +int getfree(Fs *, uvlong *); +int putfree(Fs *, uvlong); +Chan* chanattach(Fs *, int); +Chan* chanclone(Chan *); +int chanwalk(Chan *, char *); +int chancreat(Chan *, char *, int, int); +int chanopen(Chan *, int mode); +int chanwrite(Chan *, void *, ulong, uvlong); +int chanread(Chan *, void *, ulong, uvlong); +int chanstat(Chan *, Dir *); +int chanwstat(Chan *, Dir *); +int permcheck(Fs *, Dentry *, short, int); +char * uid2name(Fs *, short); +int name2uid(Fs *, char *, short *); +void start9p(char *, int); +int chanclunk(Chan *); +int chanremove(Chan *); +int getblk(Fs *, FLoc *, Buf *, uvlong, uvlong *, int); +void initcons(char *); +void shutdown(void); +int fsdump(Fs *); +int willmodify(Fs *, Loc *, int); +void chbegin(Chan *); +void chend(Chan *); +int newqid(Fs *, uvlong *); +Loc * getloc(Fs *, FLoc, Loc *); +int haveloc(Fs *, uvlong, int, Loc *); +Loc * cloneloc(Fs *, Loc *); +void putloc(Fs *, Loc *, int); +int findentry(Fs *, FLoc *, Buf *, char *, FLoc *, int); +void modified(Chan *, Dentry *); +int trunc(Fs *, FLoc *, Buf *, uvlong); +int dprint(char *fmt, ...); +int delete(Fs *, FLoc *, Buf *); +int chref(Fs *, uvlong, int); +int newentry(Fs *, Loc *, Buf *, char *, FLoc *); +int namevalid(char *); +int usersload(Fs *, Chan *); +int userssave(Fs *, Chan *); +int ingroup(Fs *, short, short, int); +void workerinit(void); +void writeusers(Fs *); diff --git a/sys/src/cmd/hjfs/fs1.c b/sys/src/cmd/hjfs/fs1.c new file mode 100644 index 000000000..4cfbc09ea --- /dev/null +++ b/sys/src/cmd/hjfs/fs1.c @@ -0,0 +1,945 @@ +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +int +chref(Fs *fs, uvlong r, int stat) +{ + uvlong i; + int j; + ulong rc; + Buf *c; + + i = fs->fstart + r / REFPERBLK; + j = r % REFPERBLK; + c = getbuf(fs->d, i, TREF, 0); + if(c == nil) + return -1; + if(stat < 0 && c->refs[j] < -stat) + c->refs[j] = 0; + else + c->refs[j] += stat; + rc = c->refs[j]; + if(stat != 0) + c->op |= BDELWRI; + putbuf(c); + return rc; +} + +int +getfree(Fs *fs, uvlong *r) +{ + Dev *d; + Buf *b, *c; + uvlong i, l; + int j, have; + + d = fs->d; + b = getbuf(d, SUPERBLK, TSUPERBLOCK, 0); + if(b == nil) + return -1; + if(nbrecv(fs->freelist, r) > 0) + goto found; + have = 0; + for(i = b->sb.fstart, l = 0; i < b->sb.fend; i++){ + c = getbuf(d, i, TREF, 0); + if(c == nil){ + putbuf(b); + return -1; + } + for(j = 0; j < REFPERBLK; j++, l++) + if(c->refs[j] == 0){ + if(!have){ + have = 1; + *r = l; + }else if(nbsend(fs->freelist, &l) <= 0){ + putbuf(c); + goto done; + } + } + putbuf(c); + } +done: + if(!have){ + werrstr("disk full"); + return -1; + } +found: + putbuf(b); + if(chref(fs, *r, 1) < 0) + return -1; + return 1; +} + +int +putfree(Fs *fs, uvlong r) +{ + int rc; + + rc = chref(fs, r, -1); + if(rc < 0) + return -1; + if(rc == 0) + nbsend(fs->freelist, &r); + return 1; +} + +static void +createroot(Fs *fs) +{ + uvlong r; + Buf *c; + Dentry *d; + + if(getfree(fs, &r) < 0) + sysfatal("ream: getfree: %r"); + c = getbuf(fs->d, r, TDENTRY, 1); + if(c == nil) + error: + sysfatal("ream: getbuf: %r"); + memset(c->de, 0, sizeof(c->de)); + d = &c->de[0]; + strcpy(d->name, "/"); + d->mode = DALLOC | 0775; + d->path = ROOTQID; + d->type = QTDIR; + d->uid = -1; + d->muid = -1; + d->gid = -1; + d->mtime = time(0); + d->atime = d->mtime; + d++; + strcpy(d->name, "/"); + d->mode = DALLOC | 0775; + d->path = ROOTQID; + d->type = QTDIR; + d->uid = -1; + d->muid = -1; + d->gid = -1; + d->mtime = time(0); + d->atime = d->mtime; + c->op |= BWRIM; + putbuf(c); + c = getbuf(fs->d, SUPERBLK, TSUPERBLOCK, 1); + if(c == nil) + goto error; + fs->root = r; + c->sb.root = r; + putbuf(c); +} + +void +writeusers(Fs *fs) +{ + Chan *ch; + + ch = chanattach(fs, 0); + if(ch == nil) + goto error; + ch->uid = -1; + chancreat(ch, "adm", DMDIR | 0755, OREAD); + chanclunk(ch); + ch = chanattach(fs, 0); + if(ch == nil) + goto error; + ch->uid = -1; + if(chanwalk(ch, "adm") <= 0) + goto error; + if(chanwalk(ch, "users") > 0){ + if(chanopen(ch, OWRITE|OTRUNC) <= 0) + goto error; + }else if(chancreat(ch, "users", 0644, OWRITE) <= 0) + goto error; + if(userssave(fs, ch) < 0){ + chanremove(ch); + ch = nil; + goto error; + } + chanclunk(ch); + return; +error: + if(ch != nil) + chanclunk(ch); + dprint("writeusers: %r\n"); +} + +static void +readusers(Fs *fs) +{ + Chan *ch; + + ch = chanattach(fs, 0); + if(ch == nil) + goto err; + ch->uid = -1; + if(chanwalk(ch, "adm") <= 0) + goto err; + if(chanwalk(ch, "users") <= 0) + goto err; + if(chanopen(ch, OREAD) < 0) + goto err; + if(usersload(fs, ch) < 0) + goto err; + chanclunk(ch); + return; +err: + if(ch != nil) + chanclunk(ch); + dprint("hjfs: readusers: %r\nhjfs: using default user db\n"); +} + +void +ream(Fs *fs) +{ + Dev *d; + Buf *b, *c; + uvlong i, firsti, lasti; + int j, je; + + d = fs->d; + dprint("hjfs: reaming %s\n", d->name); + b = getbuf(d, SUPERBLK, TSUPERBLOCK, 1); + if(b == nil) + err: + sysfatal("ream: getbuf: %r"); + memset(&b->sb, 0, sizeof(b->sb)); + b->sb.magic = SUPERMAGIC; + b->sb.size = d->size; + b->sb.fstart = SUPERBLK + 1; + fs->fstart = b->sb.fstart; + b->sb.fend = b->sb.fstart + HOWMANY(b->sb.size * 3, RBLOCK); + b->sb.qidpath = DUMPROOTQID + 1; + firsti = b->sb.fstart + SUPERBLK / REFPERBLK; + lasti = b->sb.fstart + b->sb.fend / REFPERBLK; + for(i = b->sb.fstart; i < b->sb.fend; i++){ + c = getbuf(d, i, TREF, 1); + if(c == nil) + goto err; + memset(c->refs, 0, sizeof(b->data)); + if(i >= firsti && i <= lasti){ + j = 0; + je = REFPERBLK; + if(i == firsti) + j = SUPERBLK % REFPERBLK; + if(i == lasti) + je = b->sb.fend % REFPERBLK; + for(; j < je; j++) + c->refs[j] = 1; + } + c->op |= BWRIM; + putbuf(c); + } + b->op |= BDELWRI; + putbuf(b); + createroot(fs); + sync(1); + dprint("hjfs: ream successful\n"); +} + +Fs * +initfs(Dev *d, int doream, int flags) +{ + Fs *fs; + Buf *b; + FLoc f; + + fs = emalloc(sizeof(*fs)); + fs->d = d; + if(doream) + ream(fs); + b = getbuf(d, SUPERBLK, TSUPERBLOCK, 0); + if(b == nil || b->sb.magic != SUPERMAGIC) + goto error; + fs->root = b->sb.root; + fs->fstart = b->sb.fstart; + fs->freelist = chancreate(sizeof(uvlong), FREELISTLEN); + f.blk = fs->root; + f.deind = 0; + f.path = ROOTQID; + f.vers = 0; + f.type = QTDIR; + fs->rootloc = getloc(fs, f, nil); + f.deind++; + f.path = DUMPROOTQID; + fs->dumprootloc = getloc(fs, f, nil); + putbuf(b); + fs->flags = flags; + if(doream) + writeusers(fs); + readusers(fs); + return fs; + +error: + if(b != nil) + putbuf(b); + free(fs); + return nil; +} + +void +chbegin(Chan *ch) +{ + if((ch->flags & CHFNOLOCK) == 0) + rlock(ch->fs); +} + +void +chend(Chan *ch) +{ + if((ch->flags & CHFNOLOCK) == 0) + runlock(ch->fs); +} + +int +newqid(Fs *fs, uvlong *q) +{ + Buf *b; + + b = getbuf(fs->d, SUPERBLK, TSUPERBLOCK, 0); + if(b == nil) + return -1; + *q = b->sb.qidpath++; + b->op |= BDELWRI; + putbuf(b); + return 1; +} + +Loc * +getloc(Fs *fs, FLoc f, Loc *next) +{ + Loc *l; + + qlock(&fs->loctree); + if(next != nil && next->child != nil){ + l = next->child; + do{ + if(l->blk == f.blk && l->deind == f.deind){ + l->ref++; + qunlock(&fs->loctree); + return l; + } + l = l->cnext; + }while(l != next->child); + } + l = emalloc(sizeof(*l)); + l->ref = 1; + l->FLoc = f; + l->next = next; + if(fs->rootloc != nil){ + l->gnext = fs->rootloc; + l->gprev = l->gnext->gprev; + l->gnext->gprev = l; + l->gprev->gnext = l; + }else + l->gnext = l->gprev = l; + l->cprev = l->cnext = l; + if(next != nil){ + if(next->child == nil) + next->child = l; + else{ + l->cnext = next->child; + l->cprev = next->child->cprev; + l->cnext->cprev = l; + l->cprev->cnext = l; + } + } + qunlock(&fs->loctree); + return l; +} + +int +haveloc(Fs *fs, uvlong blk, int deind, Loc *next) +{ + Loc *l; + + qlock(&fs->loctree); + l = next->child; + do{ + if(l->blk == blk && l->deind == deind){ + qunlock(&fs->loctree); + return 1; + } + l = l->cnext; + }while(l != next->child); + qunlock(&fs->loctree); + return 0; +} + +Loc * +cloneloc(Fs *fs, Loc *l) +{ + Loc *m; + + qlock(&fs->loctree); + for(m = l; m != nil; m = m->next) + m->ref++; + qunlock(&fs->loctree); + return l; +} + +void +putloc(Fs *fs, Loc *l, int loop) +{ + Loc *m; + Buf *b; + + qlock(&fs->loctree); + while(loop && l != nil && l->ref <= 1){ + if((l->flags & LGONE) != 0){ + b = getbuf(fs->d, l->blk, TDENTRY, 0); + if(b != nil){ + delete(fs, l, b); + putbuf(b); + } + } + l->cnext->cprev = l->cprev; + l->cprev->cnext = l->cnext; + l->gnext->gprev = l->gprev; + l->gprev->gnext = l->gnext; + if(l->next != nil && l->next->child == l){ + if(l->cnext == l) + l->next->child = nil; + else + l->next->child = l->cnext; + } + m = l->next; + free(l); + l = m; + } + for(; loop && l != nil; l = l->next) + --l->ref; + qunlock(&fs->loctree); +} + +static int +isopen(Fs *fs, FLoc *p, uvlong blk, int deind) +{ + Loc *l; + + qlock(&fs->loctree); + for(l = fs->rootloc->gnext; l != fs->rootloc; l = l->gnext) + if(l->blk == blk && l->deind == deind && l->next->blk == p->blk && l->next->deind == p->deind){ + qunlock(&fs->loctree); + return 1; + } + qunlock(&fs->loctree); + return 0; +} + +static int +dumpblk(Fs *fs, FLoc *p, uvlong *l) +{ + uvlong n; + int i; + Buf *b, *c; + Dentry *d; + + b = getbuf(fs->d, *l, TDONTCARE, 0); + if(b == nil) + return -1; + if(getfree(fs, &n) < 0){ + berr: + putbuf(b); + return -1; + } + c = getbuf(fs->d, n, b->type, 1); + if(c == nil){ + putfree(fs, n); + goto berr; + } + switch(b->type){ + case TINDIR: + memcpy(c->offs, b->offs, sizeof(b->offs)); + for(i = 0; i < OFFPERBLK; i++) + if(b->offs[i] != 0) + chref(fs, b->offs[i], 1); + break; + case TRAW: + memcpy(c->data, b->data, sizeof(b->data)); + break; + case TDENTRY: + memcpy(c->de, b->de, sizeof(b->de)); + for(d = b->de; d < &b->de[DEPERBLK]; d++){ + if((d->mode & DGONE) != 0 && !isopen(fs, p, *l, d - b->de)) + memset(d, 0, sizeof(Dentry)); + if((d->mode & DALLOC) == 0) + continue; + if((d->type & QTTMP) != 0) + continue; + for(i = 0; i < NDIRECT; i++) + if(d->db[i] != 0) + chref(fs, d->db[i], 1); + for(i = 0; i < NINDIRECT; i++) + if(d->ib[i] != 0) + chref(fs, d->ib[i], 1); + } + break; + default: + werrstr("dumpblk -- unknown type %d", b->type); + putfree(fs, n); + putbuf(c); + putbuf(b); + return -1; + } + c->op |= BDELWRI; + putbuf(c); + putbuf(b); + putfree(fs, *l); + *l = n; + return 0; +} + +/* + * getblk returns the address of a block in a file + * given its relative address blk + * the address and generation are returned in *r and *gen + * mode has to be one of: + * - GBREAD: this block will only be read + * - GBWRITE: this block will be written, but don't create it + * if it doesn't exist + * - GBCREATE: this block will be written, create it if necessary + * - GBOVERWR: like GBCREATE, but return an empty block if a dump + * would be necessary + * return value is 1 if the block existed, -1 on error + * a return value of 0 means the block did not exist + * this is only an error in case of GBREAD and GBWRITE + * gen == nil allowed + */ + +int +getblk(Fs *fs, FLoc *L, Buf *bd, uvlong blk, uvlong *r, int mode) +{ + uvlong k, l; + uvlong *loc; + int i, j, rc, prc; + Buf *b; + Dentry *d; + + b = bd; + d = &bd->de[L->deind]; + if(blk < NDIRECT){ + loc = &d->db[blk]; + goto found; + } + blk -= NDIRECT; + l = 1; + for(i = 0; i < NINDIRECT; i++){ + l *= OFFPERBLK; + if(blk < l) + break; + blk -= l; + } + if(i == NINDIRECT){ + werrstr("getblk: block offset too large"); + return -1; + } + loc = &d->ib[i]; + for(j = 0; j <= i; j++){ + if(*loc == 0){ + if(mode == GBREAD || mode == GBWRITE){ + if(b != bd) + putbuf(b); + return 0; + } + if(getfree(fs, loc) < 0){ + if(b != bd) + putbuf(b); + return -1; + } + b->op |= BDELWRI; + k = *loc; + if(b != bd) + putbuf(b); + b = getbuf(fs->d, k, TINDIR, 1); + if(b == nil) + return -1; + memset(b->offs, 0, sizeof(b->offs)); + }else{ + if(mode != GBREAD && chref(fs, *loc, 0) > 1) + if(dumpblk(fs, L, loc) < 0){ + if(b != bd) + putbuf(b); + return -1; + } + k = *loc; + if(b != bd) + putbuf(b); + b = getbuf(fs->d, k, TINDIR, 0); + if(b == nil) + return -1; + } + l /= OFFPERBLK; + loc = &b->offs[blk / l]; + blk %= l; + } + +found: + rc = 0; + prc = 0; + if(*loc != 0){ + if(mode == GBREAD) + goto okay; + if((rc = chref(fs, *loc, 0)) > 1){ + if(mode == GBOVERWR){ + putfree(fs, *loc); + *loc = 0; + b->op |= BDELWRI; + prc = 1; + goto new; + } + if(dumpblk(fs, L, loc) < 0){ + rc = -1; + goto end; + } + b->op |= BDELWRI; + } + if(rc < 0) + goto end; + if(rc == 0){ + dprint("hjfs: getblk: block %lld has refcount 0\n"); + werrstr("phase error -- getblk"); + rc = -1; + goto end; + } +okay: + *r = *loc; + rc = 1; + }else if(mode == GBCREATE || mode == GBOVERWR){ +new: + rc = getfree(fs, r); + if(rc > 0){ + *loc = *r; + b->op |= BDELWRI; + rc = prc; + } + } +end: + if(b != bd) + putbuf(b); + return rc; +} + +static void +delindir(Fs *fs, uvlong *l, int n) +{ + Buf *b; + int k; + + if(*l == 0) + return; + if(chref(fs, *l, 0) > 1){ + *l = 0; + return; + } + if(n > 0){ + b = getbuf(fs->d, *l, TINDIR, 0); + if(b != nil){ + for(k = 0; k < OFFPERBLK; k++) + if(b->offs[k] != 0) + delindir(fs, &b->offs[k], n-1); + b->op |= BDELWRI; + putbuf(b); + } + } + putfree(fs, *l); + *l = 0; +} + +static int +zeroes(int *a, int i) +{ + while(i--) + if(*a++ != 0) + return 0; + return 1; +} + +static void +delindirpart(Fs *fs, FLoc *p, uvlong *l, int *a, int n) +{ + Buf *b; + int k; + + if(*l == 0) + return; + if(n == 0){ + putfree(fs, *l); + *l = 0; + return; + } + if(zeroes(a, n)){ + delindir(fs, l, n); + return; + } + if(chref(fs, *l, 0) > 1) + dumpblk(fs, p, l); + b = getbuf(fs->d, *l, TINDIR, 0); + if(b == nil) + return; + delindirpart(fs, p, &b->offs[*a], a + 1, n - 1); + for(k = a[0] + 1; k < OFFPERBLK; k++) + if(b->offs[k] != 0) + delindir(fs, &b->offs[k], n - 1); + b->op |= BDELWRI; + putbuf(b); +} + +/* + * call willmodify() before and modified() + * after calling this function + */ +int +trunc(Fs *fs, FLoc *ll, Buf *bd, uvlong size) +{ + uvlong blk; + Dentry *d; + int a[NINDIRECT]; + uvlong l; + int i, j; + + d = &bd->de[ll->deind]; + if(size >= d->size) + goto done; + blk = HOWMANY(size, RBLOCK); + while(blk < NDIRECT){ + if(d->db[blk] != 0){ + putfree(fs, d->db[blk]); + d->db[blk] = 0; + } + blk++; + } + blk -= NDIRECT; + l = 1; + for(i = 0; i < NINDIRECT; i++){ + l *= OFFPERBLK; + if(blk < l) + break; + blk -= l; + } + if(blk >= l){ + werrstr("phase error -- truncate"); + return -1; + } + if(blk == 0) + goto rest; + if(d->ib[i] == 0){ + i++; + goto rest; + } + for(j = 0; j <= i; j++){ + l /= OFFPERBLK; + a[j] = blk / l; + blk %= l; + } + delindirpart(fs, ll, &d->ib[i], a, i + 1); + i++; +rest: + for(; i < NINDIRECT; i++) + delindir(fs, &d->ib[i], i + 1); +done: + d->size = size; + bd->op |= BDELWRI; + return 1; +} + +/* + * find a direntry + * name == nil allows any entry to match + * rl == nil allowed + * return value 1 on success, 0 on Enoent, + * -1 on other errors + */ +int +findentry(Fs *fs, FLoc *l, Buf *b, char *name, FLoc *rl, int dump) +{ + uvlong i; + int j; + Dentry *d; + uvlong r; + Buf *c; + + d = &b->de[l->deind]; + for(i = 0; i < d->size; i++){ + if(getblk(fs, l, b, i, &r, GBREAD) <= 0) + continue; + c = getbuf(fs->d, r, TDENTRY, 0); + if(c == nil) + continue; + for(j = 0; j < DEPERBLK; j++) + if((c->de[j].mode & DALLOC) != 0 && + (name == nil || strcmp(c->de[j].name, name) == 0)){ + if(dump && (c->de[j].type & QTTMP) != 0) + continue; + if(rl != nil){ + rl->Qid = c->de[j].Qid; + rl->blk = r; + rl->deind = j; + } + putbuf(c); + return 1; + } + putbuf(c); + } + werrstr(Enoent); + return 0; +} + +void +modified(Chan *ch, Dentry *d) +{ + d->mtime = time(0); + d->atime = d->mtime; + d->muid = ch->uid; + ch->loc->vers = ++d->vers; +} + +typedef struct Del Del; + +struct Del { + FLoc; + Del *next, *prev; +}; + +static int +deltraverse(Fs *fs, Del *p, Buf *b, Del **last) +{ + Buf *c; + int frb; + Dentry *d; + uvlong i, s, r; + int j, rc; + Del *dd; + + frb = b == nil; + if(b == nil){ + b = getbuf(fs->d, p->blk, TDENTRY, 0); + if(b == nil) + return -1; + } + s = b->de[p->deind].size; + for(i = 0; i < s; i++){ + rc = getblk(fs, p, b, i, &r, GBREAD); + if(rc <= 0) + continue; + c = getbuf(fs->d, r, TDENTRY, 0); + if(c == nil) + continue; + for(j = 0; j < DEPERBLK; j++){ + d = &c->de[j]; + if((d->mode & (DALLOC|DGONE)) != 0){ + if((d->type & QTDIR) == 0){ + trunc(fs, p, b, 0); + memset(d, 0, sizeof(*d)); + c->op |= BDELWRI; + }else{ + dd = emalloc(sizeof(Del)); + dd->blk = i; + dd->deind = j; + dd->prev = *last; + (*last)->next = dd; + *last = dd; + } + } + } + putbuf(c); + } + if(frb) + putbuf(b); + return 0; +} + +int +delete(Fs *fs, FLoc *l, Buf *b) +{ + Dentry *d; + Buf *c; + Del *first, *last, *p, *q; + + d = &b->de[l->deind]; + if((d->type & QTDIR) == 0){ + trunc(fs, l, b, 0); + memset(d, 0, sizeof(*d)); + return 0; + } + first = last = emalloc(sizeof(Del)); + first->FLoc = *l; + for(p = first; p != nil; p = p->next) + deltraverse(fs, p, p == first ? b : nil, &last); + for(p = last; p != nil; q = p->prev, free(p), p = q){ + if(p == first) + c = b; + else + c = getbuf(fs->d, p->blk, TDENTRY, 0); + if(c == nil) + continue; + trunc(fs, p, c, 0); + memset(&c->de[p->deind], 0, sizeof(Dentry)); + c->op |= BDELWRI; + if(p != first) + putbuf(c); + } + return 0; +} + +int +newentry(Fs *fs, Loc *l, Buf *b, char *name, FLoc *res) +{ + Dentry *d; + uvlong i, si, r; + int j, sj, rc; + Buf *c; + FLoc f; + + si = sj = -1; + d = &b->de[l->deind]; + for(i = 0; i <= d->size; i++){ + if(i == d->size && si != -1) + break; + rc = getblk(fs, l, b, i, &r, si == -1 ? GBCREATE : GBREAD); + if(rc < 0) + continue; + if(rc == 0 && si != -1) + continue; + c = getbuf(fs->d, r, TDENTRY, rc == 0); + if(c == nil) + continue; + if(rc == 0){ + memset(c->de, 0, sizeof(c->de)); + if(i == d->size) + d->size++; + } + for(j = 0; j < DEPERBLK; j++){ + if(si == -1 && (c->de[j].mode & DALLOC) == 0){ + si = i; + sj = j; + } + if(si == -1 && (c->de[j].mode & DGONE) != 0 && !haveloc(fs, r, j, l)){ + memset(&f, 0, sizeof(f)); + f.blk = r; + f.deind = j; + if(delete(fs, &f, c) >= 0){ + si = i; + sj = j; + } + } + if((c->de[j].mode & DALLOC) != 0 && + strcmp(c->de[j].name, name) == 0){ + werrstr(Eexists); + putbuf(c); + return 0; + } + } + putbuf(c); + } + if(si == -1 || sj == -1){ + werrstr("phase error -- create"); + return -1; + } + if(getblk(fs, l, b, si, &res->blk, GBWRITE) <= 0) + return -1; + res->deind = sj; + return 1; +} diff --git a/sys/src/cmd/hjfs/fs2.c b/sys/src/cmd/hjfs/fs2.c new file mode 100644 index 000000000..747932e7d --- /dev/null +++ b/sys/src/cmd/hjfs/fs2.c @@ -0,0 +1,742 @@ +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +Chan * +chanattach(Fs *fs, int flags) +{ + Chan *ch; + + ch = emalloc(sizeof(*ch)); + ch->fs = fs; + ch->flags = flags; + ch->loc = cloneloc(fs, (flags & CHFDUMP) != 0 ? fs->dumprootloc : fs->rootloc); + return ch; +} + +Chan * +chanclone(Chan *ch) +{ + Chan *d; + + chbegin(ch); + d = emalloc(sizeof(*d)); + d->fs = ch->fs; + d->flags = ch->flags; + d->uid = ch->uid; + d->loc = cloneloc(ch->fs, ch->loc); + chend(ch); + return d; +} + +int +chanwalk(Chan *ch, char *name) +{ + Buf *b; + Dentry *d; + Loc *l; + FLoc f; + + if(name == nil || name[0] == 0 || name[0] == '.' && name[1] == 0) + return 1; + chbegin(ch); + b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0); + if(b == nil){ + chend(ch); + return -1; + } + d = &b->de[ch->loc->deind]; + if((d->type & QTDIR) == 0){ + werrstr(Enotadir); + goto error; + } + if(!permcheck(ch->fs, d, ch->uid, OEXEC)){ + werrstr(Eperm); + goto error; + } + if(strcmp(name, "..") == 0){ + l = ch->loc->next; + if(l == nil) + goto done; + putloc(ch->fs, ch->loc, 0); + ch->loc = l; + goto done; + } + if(findentry(ch->fs, ch->loc, b, name, &f, ch->flags & CHFDUMP) <= 0) + goto error; + ch->loc = getloc(ch->fs, f, ch->loc); +done: + putbuf(b); + chend(ch); + return 1; +error: + putbuf(b); + chend(ch); + return -1; +} + +int +namevalid(char *name) +{ + char *p; + + if(name == nil || name[0] == 0) + return 0; + if(name[0] == '.' && (name[1] == 0 || name[1] == '.' && name[2] == 0)) + return 0; + for(p = name; *p; p++) + if((uchar) *p < ' ' || *p == '/') + return 0; + return p - name < NAMELEN; +} + + +int +chancreat(Chan *ch, char *name, int perm, int mode) +{ + Buf *b, *c; + Dentry *d; + int isdir; + FLoc f; + + if((ch->flags & CHFRO) != 0){ + werrstr(Einval); + return -1; + } + chbegin(ch); + if(willmodify(ch->fs, ch->loc, ch->flags & CHFNOLOCK) < 0){ + chend(ch); + return -1; + } + if(!namevalid || ch->open != 0){ + werrstr(Einval); + chend(ch); + return -1; + } + if(isdir = ((perm & DMDIR) != 0)) + if((mode & (OWRITE | OEXEC | ORCLOSE | OTRUNC)) != 0){ + werrstr(Einval); + chend(ch); + return -1; + } + b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0); + if(b == nil){ + chend(ch); + return -1; + } + d = &b->de[ch->loc->deind]; + if((d->type & QTDIR) == 0){ + werrstr(Enotadir); + goto error; + } + if(!permcheck(ch->fs, d, ch->uid, OWRITE)){ + werrstr(Eperm); + goto error; + } + if(newentry(ch->fs, ch->loc, b, name, &f) <= 0) + goto error; + c = getbuf(ch->fs->d, f.blk, TDENTRY, 0); + if(c == nil) + goto error; + modified(ch, d); + if(isdir) + perm &= ~0777 | d->mode & 0777; + else + perm &= ~0666 | d->mode & 0666; + d = &c->de[f.deind]; + memset(d, 0, sizeof(Dentry)); + if(newqid(ch->fs, &d->path) < 0) + goto error; + d->type = perm >> 24; + strcpy(d->name, name); + d->mtime = time(0); + d->atime = d->mtime; + d->gid = d->uid = d->muid = ch->uid; + d->mode = DALLOC | perm & 0777; + f.Qid = d->Qid; + ch->loc = getloc(ch->fs, f, ch->loc); + if((d->type & QTEXCL) != 0){ + qlock(&ch->loc->ex); + ch->loc->exlock = ch; + ch->loc->lwrite = d->atime; + qunlock(&ch->loc->ex); + } + c->op |= BDELWRI; + b->op |= BDELWRI; + putbuf(c); + putbuf(b); + switch(mode & OEXEC){ + case ORDWR: + ch->open |= CHREAD; + case OWRITE: + ch->open |= CHWRITE; + break; + case OEXEC: + case OREAD: + ch->open |= CHREAD; + break; + } + chend(ch); + return 1; + +error: + putbuf(b); + chend(ch); + return -1; +} + +int +chanopen(Chan *ch, int mode) +{ + Buf *b; + Dentry *d; + int isdir; + + chbegin(ch); + if(ch->open != 0){ + werrstr(Einval); + chend(ch); + return -1; + } + b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0); + if(b == nil){ + chend(ch); + return -1; + } + d = &b->de[ch->loc->deind]; + if(!permcheck(ch->fs, d, ch->uid, mode & OEXEC)){ + permerr: + werrstr(Eperm); + err: + putbuf(b); + chend(ch); + return -1; + } + if((d->type & QTAPPEND) != 0) + mode &= ~OTRUNC; + isdir = (d->type & QTDIR) != 0; + if(isdir && (mode & (ORCLOSE | OTRUNC | OWRITE | ORDWR)) != 0){ + werrstr(Einval); + goto err; + } + if((mode & OTRUNC) != 0 && !permcheck(ch->fs, d, ch->uid, OWRITE)) + goto permerr; + if((ch->flags & CHFRO) != 0 && (mode & (ORCLOSE | OTRUNC | OWRITE | ORDWR)) != 0){ + werrstr(Einval); + goto err; + } + if((ch->loc->type & QTEXCL) != 0){ + qlock(&ch->loc->ex); + if(ch->loc->exlock == nil || ch->loc->lwrite < time(0) - EXCLDUR){ + ch->loc->exlock = ch; + ch->loc->lwrite = time(0); + qunlock(&ch->loc->ex); + }else{ + qunlock(&ch->loc->ex); + werrstr(Elocked); + goto err; + } + } + switch(mode & OEXEC){ + case ORDWR: + ch->open |= CHREAD; + case OWRITE: + ch->open |= CHWRITE; + break; + case OEXEC: + case OREAD: + ch->open |= CHREAD; + break; + } + if((mode & OTRUNC) != 0){ + trunc(ch->fs, ch->loc, b, 0); + modified(ch, d); + } + if((mode & ORCLOSE) != 0) + ch->open |= CHRCLOSE; + putbuf(b); + chend(ch); + return 1; +} + +static int +checklock(Chan *ch) +{ + int rc; + + qlock(&ch->loc->ex); + rc = 1; + if(ch->loc->exlock == ch){ + if(ch->loc->lwrite < time(0) - EXCLDUR){ + ch->loc->exlock = nil; + werrstr("lock broken"); + rc = -1; + }else + ch->loc->lwrite = time(0); + }else{ + werrstr(Elocked); + rc = -1; + } + qunlock(&ch->loc->ex); + return rc; +} + +int +chanwrite(Chan *ch, void *buf, ulong n, uvlong off) +{ + uvlong i, e, bl; + int r, rn, rc; + Buf *b, *c; + Dentry *d; + uchar *p; + + if(n == 0) + return 0; + if((ch->flags & CHFRO) != 0){ + werrstr(Einval); + return -1; + } + if((ch->open & CHWRITE) == 0){ + werrstr(Einval); + return -1; + } + chbegin(ch); + if((ch->loc->type & QTEXCL) != 0 && checklock(ch) < 0){ + chend(ch); + return -1; + } + if(willmodify(ch->fs, ch->loc, ch->flags & CHFNOLOCK) < 0){ + chend(ch); + return -1; + } + b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0); + if(b == nil){ + chend(ch); + return -1; + } + d = &b->de[ch->loc->deind]; + if((d->type & QTAPPEND) != 0) + off = d->size; + e = off + n; + i = off; + p = buf; + while(i < e){ + bl = i / RBLOCK; + r = i % RBLOCK; + rn = RBLOCK - r; + if(i + rn > e) + rn = e - i; + rc = getblk(ch->fs, ch->loc, b, bl, &bl, rn == RBLOCK ? GBOVERWR : GBCREATE); + if(rc < 0) + goto done; + c = getbuf(ch->fs->d, bl, TRAW, rc == 0 || rn == RBLOCK); + if(c == nil) + goto done; + if(rc == 0 && rn != RBLOCK) + memset(c->data, 0, sizeof(c->data)); + memcpy(c->data + r, p, rn); + i += rn; + c->op |= i != e ? BWRIM : BDELWRI; + putbuf(c); + p += rn; + } +done: + modified(ch, d); + e = off + (p - (uchar *) buf); + if(e > d->size) + d->size = e; + b->op |= BDELWRI; + putbuf(b); + chend(ch); + if(p == buf) + return -1; + return p - (uchar *) buf; +} + +static int chandirread(Chan *, void *, ulong, uvlong); + +int +chanread(Chan *ch, void *buf, ulong n, uvlong off) +{ + uvlong i, e, bl; + int r, rn, rc; + uchar *p; + Buf *b, *c; + Dentry *d; + + chbegin(ch); + if((ch->loc->type & QTEXCL) != 0 && checklock(ch) < 0){ + chend(ch); + return -1; + } + if((ch->open & CHREAD) == 0){ + werrstr(Einval); + chend(ch); + return -1; + } + if((ch->loc->Qid.type & QTDIR) != 0) + return chandirread(ch, buf, n, off); + b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0); + if(b == nil){ + chend(ch); + return -1; + } + d = &b->de[ch->loc->deind]; + if(off >= d->size) + n = 0; + else if(off + n > d->size) + n = d->size - off; + if(n == 0){ + putbuf(b); + chend(ch); + return 0; + } + e = off + n; + i = off; + p = buf; + while(i < e){ + bl = i / RBLOCK; + r = i % RBLOCK; + rn = RBLOCK - r; + if(i + rn > e) + rn = e - i; + rc = getblk(ch->fs, ch->loc, b, bl, &bl, GBREAD); + if(rc < 0) + goto error; + if(rc == 0) + memset(p, 0, rn); + else{ + c = getbuf(ch->fs->d, bl, TRAW, 0); + if(c == nil) + goto error; + memcpy(p, c->data + r, rn); + putbuf(c); + } + i += rn; + p += rn; + } + putbuf(b); + chend(ch); + return n; + +error: + putbuf(b); + chend(ch); + return -1; +} + +static void +statbuf(Fs *fs, Dentry *d, Dir *di) +{ + di->qid = d->Qid; + di->mode = (d->mode & 0777) | (d->Qid.type << 24); + di->mtime = d->mtime; + di->atime = d->atime; + di->length = d->size; + if(d->type & QTDIR) + di->length = 0; + di->name = strdup(d->name); + di->uid = uid2name(fs, d->uid); + di->gid = uid2name(fs, d->gid); + di->muid = uid2name(fs, d->muid); +} + +int +chanstat(Chan *ch, Dir *di) +{ + Buf *b; + + chbegin(ch); + b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0); + if(b == nil){ + chend(ch); + return -1; + } + statbuf(ch->fs, &b->de[ch->loc->deind], di); + putbuf(b); + chend(ch); + return 0; +} + +static int +chandirread(Chan *ch, void *buf, ulong n, uvlong off) +{ + Buf *b, *c; + Dentry *d; + uvlong i, blk; + int j; + int rc; + ulong wr; + Dir di; + + if(off == 0){ + ch->dwloff = 0; + ch->dwblk = 0; + ch->dwind = 0; + }else if(ch->dwloff != off){ + werrstr(Einval); + chend(ch); + return -1; + } + b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0); + if(b == nil){ + chend(ch); + return -1; + } + d = &b->de[ch->loc->deind]; + if(ch->dwblk >= d->size){ + putbuf(b); + chend(ch); + return 0; + } + c = nil; + wr = 0; + i = ch->dwblk; + j = ch->dwind; + for(;;){ + if(c == nil){ + rc = getblk(ch->fs, ch->loc, b, i, &blk, GBREAD); + if(rc < 0) + goto error; + if(rc == 0){ + j = 0; + if(++i >= d->size) + break; + continue; + } + c = getbuf(ch->fs->d, blk, TDENTRY, 0); + if(c == nil) + goto error; + } + if((c->de[j].mode & DALLOC) == 0) + goto next; + if((ch->flags & CHFDUMP) != 0 && (c->de[j].type & QTTMP) != 0) + goto next; + statbuf(ch->fs, &c->de[j], &di); + rc = convD2M(&di, (uchar *) buf + wr, n - wr); + free(di.uid); + free(di.gid); + free(di.muid); + if(rc <= BIT16SZ) + break; + wr += rc; + next: + if(++j >= DEPERBLK){ + j = 0; + if(c != nil) + putbuf(c); + c = nil; + if(++i >= d->size) + break; + } + } + ch->dwblk = i; + ch->dwind = j; + ch->dwloff += wr; + if(c != nil) + putbuf(c); + putbuf(b); + chend(ch); + return wr; +error: + putbuf(b); + chend(ch); + return -1; +} + +int +chanclunk(Chan *ch) +{ + Buf *b, *p; + int rc; + Dentry *d; + + chbegin(ch); + rc = 1; + if(ch->open & CHRCLOSE){ + if((ch->flags & CHFRO) != 0) + goto inval; + if(willmodify(ch->fs, ch->loc, ch->flags & CHFNOLOCK) < 0) + goto err; + if(ch->loc->next == nil){ + inval: + werrstr(Einval); + err: + rc = -1; + goto done; + } + p = getbuf(ch->fs->d, ch->loc->next->blk, TDENTRY, 0); + if(p == nil) + goto err; + b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0); + if(b == nil){ + putbuf(p); + goto err; + } + if(!permcheck(ch->fs, &p->de[ch->loc->next->deind], ch->uid, OWRITE)){ + werrstr(Eperm); + putbuf(b); + putbuf(p); + goto err; + } + d = &b->de[ch->loc->deind]; + if((d->type & QTDIR) != 0 && findentry(ch->fs, ch->loc, b, nil, nil, ch->flags & CHFDUMP) != 0){ + putbuf(b); + putbuf(p); + goto inval; + } + if((d->mode & DGONE) != 0){ + putbuf(b); + putbuf(p); + goto done; + } + qlock(&ch->fs->loctree); + if(ch->loc->ref > 1){ + d->mode &= ~DALLOC; + d->mode |= DGONE; /* aaaaand it's gone */ + ch->loc->flags |= LGONE; + qunlock(&ch->fs->loctree); + }else{ + ch->loc->flags &= ~LGONE; + qunlock(&ch->fs->loctree); + rc = delete(ch->fs, ch->loc, b); + } + b->op |= BDELWRI; + putbuf(b); + putbuf(p); + } +done: + if((ch->loc->type & QTEXCL) != 0){ + qlock(&ch->loc->ex); + if(ch->loc->exlock == ch) + ch->loc->exlock = nil; + qunlock(&ch->loc->ex); + } + putloc(ch->fs, ch->loc, 1); + chend(ch); + free(ch); + return rc; +} + +int +chanwstat(Chan *ch, Dir *di) +{ + Buf *b, *pb; + Dentry *d; + int isdir, owner, rc; + short nuid, ngid; + + if((ch->flags & CHFRO) != 0){ + werrstr(Einval); + return -1; + } + chbegin(ch); + if(willmodify(ch->fs, ch->loc, ch->flags & CHFNOLOCK) < 0){ + chend(ch); + return -1; + } + pb = nil; + if(*di->name){ + if(!namevalid(di->name) || ch->loc->next == nil){ + werrstr(Einval); + chend(ch); + return -1; + } + pb = getbuf(ch->fs->d, ch->loc->next->blk, TDENTRY, 0); + if(pb == nil){ + chend(ch); + return -1; + } + if(!permcheck(ch->fs, &pb->de[ch->loc->next->deind], ch->uid, OWRITE)){ + werrstr(Eperm); + putbuf(pb); + chend(ch); + return -1; + } + rc = findentry(ch->fs, ch->loc->next, pb, di->name, nil, ch->flags & CHFDUMP); + if(rc > 0) + werrstr(Eexists); + if(rc != 0){ + putbuf(pb); + chend(ch); + return -1; + } + } + b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0); + if(b == nil){ + chend(ch); + return -1; + } + d = &b->de[ch->loc->deind]; + isdir = (d->type & QTDIR) != 0; + owner = ch->uid == d->uid || ingroup(ch->fs, ch->uid, d->gid, 1) || (ch->fs->flags & FSNOPERM) != 0; + if((uvlong)~di->length){ + if(isdir && di->length != 0) + goto inval; + if(di->length != d->size && !permcheck(ch->fs, d, ch->uid, OWRITE)) + goto perm; + } + if((ulong)~di->atime) + goto inval; + if((ulong)~di->mtime && !owner) + goto perm; + if((ulong)~di->mode && !owner) + goto perm; + nuid = NOUID; + ngid = NOUID; + if(*di->uid != 0 && name2uid(ch->fs, di->uid, &nuid) < 0) + goto inval; + if(*di->gid != 0 && name2uid(ch->fs, di->gid, &ngid) < 0) + goto inval; + if(nuid != NOUID && (ch->fs->flags & FSCHOWN) == 0) + goto inval; + if((nuid != NOUID || ngid != NOUID) && !owner) + goto perm; + + if((uvlong)~di->length && !isdir){ + trunc(ch->fs, ch->loc, b, di->length); + modified(ch, d); + } + if((ulong)~di->mtime) + d->mtime = di->mtime; + if((ulong)~di->mode){ + d->mode = d->mode & ~0777 | di->mode & 0777; + ch->loc->type = d->type = di->mode >> 24; + } + if(*di->name){ + memset(d->name, 0, NAMELEN); + strcpy(d->name, di->name); + } + if(nuid != NOUID) + d->uid = nuid; + if(ngid != NOUID) + d->gid = ngid; + b->op |= BDELWRI; + if(pb != nil) + putbuf(pb); + putbuf(b); + chend(ch); + return 1; + +inval: + werrstr(Einval); + goto error; +perm: + werrstr(Eperm); +error: + if(pb != nil) + putbuf(pb); + putbuf(b); + chend(ch); + return -1; +} + +int +chanremove(Chan *ch) +{ + ch->open |= CHRCLOSE; + return chanclunk(ch); +} diff --git a/sys/src/cmd/hjfs/main.c b/sys/src/cmd/hjfs/main.c new file mode 100644 index 000000000..de41f64eb --- /dev/null +++ b/sys/src/cmd/hjfs/main.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +char Eio[] = "i/o error"; +char Enotadir[] = "not a directory"; +char Enoent[] = "not found"; +char Einval[] = "invalid operation"; +char Eperm[] = "permission denied"; +char Eexists[] = "file exists"; +char Elocked[] = "file locked"; + +void* +emalloc(int c) +{ + void *v; + + v = mallocz(c, 1); + if(v == 0) + sysfatal("malloc: %r"); + setmalloctag(v, getcallerpc(&c)); + return v; +} + +ThrData * +getthrdata(void) +{ + ThrData **v; + + v = (ThrData **) threaddata(); + if(*v == nil){ + *v = emalloc(sizeof(**v)); + (*v)->resp = chancreate(sizeof(void *), 0); + } + return *v; +} + +Fs *fsmain; + +int +dprint(char *fmt, ...) +{ + va_list va; + int rc; + + va_start(va, fmt); + rc = vfprint(2, fmt, va); + va_end(va); + return rc; +} + +static void +syncproc(void *) +{ + for(;;){ + sync(0); + sleep(SYNCINTERVAL); + } +} + +void +usage(void) +{ + fprint(2, "usage: %s [-rsS] [-m mem] [-n service] -f dev\n", argv0); + exits("usage"); +} + +void +threadmain(int argc, char **argv) +{ + Dev *d; + char *file, *service; + int doream, flags, stdio, nbuf; + + doream = 0; + stdio = 0; + flags = FSNOAUTH; + service = "fs"; + file = nil; + nbuf = 10; + ARGBEGIN { + case 'A': flags &= ~FSNOAUTH; break; + case 'r': doream++; break; + case 'S': flags |= FSNOPERM | FSCHOWN; break; + case 's': stdio++; break; + case 'f': file = strdup(EARGF(usage())); break; + case 'n': service = strdup(EARGF(usage())); break; + case 'm': + nbuf = muldiv(atoi(EARGF(usage())), 1048576, sizeof(Buf)); + if(nbuf < 10) + nbuf = 10; + break; + default: usage(); + } ARGEND; + rfork(RFNOTEG); + bufinit(nbuf); + if(file == nil) + sysfatal("no default file"); + if(argc != 0) + usage(); + d = newdev(file); + if(d == nil) + sysfatal("newdev: %r"); + fsmain = initfs(d, doream, flags); + if(fsmain == nil) + sysfatal("fsinit: %r"); + initcons(service); + proccreate(syncproc, nil, mainstacksize); + start9p(service, stdio); + threadexits(nil); +} + +void +shutdown(void) +{ + wlock(fsmain); + sync(1); + dprint("hjfs: ending\n"); + sleep(1000); + sync(1); + threadexitsall(nil); +} diff --git a/sys/src/cmd/hjfs/mkfile b/sys/src/cmd/hjfs/mkfile new file mode 100644 index 000000000..b7064affc --- /dev/null +++ b/sys/src/cmd/hjfs/mkfile @@ -0,0 +1,21 @@ +