ref: b21b9ba89cf66a8fac6f94efb79cfb425a2c4df2
parent: ef1c1863051d0530a31b291f4e334ee7601c318c
author: aiju <[email protected]>
date: Tue Aug 7 13:57:04 EDT 2012
added hjfs
--- a/rc/bin/fstype
+++ b/rc/bin/fstype
@@ -19,6 +19,10 @@
echo paqfs
exit
}
+if(~ $"m 011ce50d){
+ echo hjfs
+ exit
+}
dd -if $1 -count 1 >[2]/dev/null | \
awk '
/^kfs/{fs["kfs"]++}
--- a/rc/bin/inst/bootsetup
+++ b/rc/bin/inst/bootsetup
@@ -37,7 +37,7 @@
bootfile=9pcf
@{
echo 'bootfile='^$bootfile
- echo 'bootargs=local!'^$fs
+ echo 'bootargs=local!'^$fs^$fsflags
if(~ $#nvram 1)
echo 'nvram='^$nvram
echo 'mouseport='^$mouseport
--- a/rc/bin/inst/configfs
+++ b/rc/bin/inst/configfs
@@ -14,8 +14,9 @@
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
}
--- a/rc/bin/inst/mountcwfs
+++ b/rc/bin/inst/mountcwfs
@@ -22,6 +22,8 @@
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)
--- 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
--- /dev/null
+++ b/rc/bin/inst/mounthjfs
@@ -1,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
+}
--- a/rc/bin/inst/prepdisk
+++ b/rc/bin/inst/prepdisk
@@ -7,6 +7,8 @@
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
}
}
--- a/sys/src/9/port/bootfs.proto
+++ b/sys/src/9/port/bootfs.proto
@@ -24,6 +24,7 @@
mntgen
mount
mv
+ hjfs
rc
rm
sed
--- /dev/null
+++ b/sys/src/cmd/hjfs/9p.c
@@ -1,0 +1,250 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <fcall.h>
+#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);
+}
--- /dev/null
+++ b/sys/src/cmd/hjfs/auth.c
@@ -1,0 +1,508 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#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;
+}
--- /dev/null
+++ b/sys/src/cmd/hjfs/buf.c
@@ -1,0 +1,314 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#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);
+ }
+ }
+}
--- /dev/null
+++ b/sys/src/cmd/hjfs/cons.c
@@ -1,0 +1,238 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+#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);
+}
--- /dev/null
+++ b/sys/src/cmd/hjfs/conv.c
@@ -1,0 +1,137 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#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);
+}
+
--- /dev/null
+++ b/sys/src/cmd/hjfs/dat.h
@@ -1,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<<BUFHASHBITS)-1,
+ NWORKERS = 5,
+ EXCLDUR = 300,
+
+ NOUID = (short)0x8000,
+};
+
+typedef struct Fs Fs;
+typedef struct Buf Buf;
+typedef struct Dev Dev;
+typedef struct BufReq BufReq;
+typedef struct ThrData ThrData;
+typedef struct Superblock Superblock;
+typedef struct Dentry Dentry;
+typedef struct Chan Chan;
+typedef struct FLoc FLoc;
+typedef struct Loc Loc;
+typedef struct User User;
+
+#pragma incomplete struct User
+#pragma varargck type "T" int
+#pragma varargck type "T" uint
+enum {
+ TRAW,
+ TSUPERBLOCK,
+ TDENTRY,
+ TINDIR,
+ TREF,
+ TDONTCARE = -1,
+};
+
+struct Superblock {
+ ulong magic;
+ uvlong size;
+ uvlong fstart;
+ uvlong fend;
+ uvlong root;
+ uvlong qidpath;
+};
+
+enum {
+ DALLOC = 1<<15,
+ DGONE = 1<<14,
+};
+
+struct Dentry {
+ char name[NAMELEN];
+ short uid;
+ short muid;
+ short gid;
+ ushort mode;
+ Qid;
+ uvlong size; /* bytes for files and blocks for dirs */
+ uvlong db[NDIRECT];
+ uvlong ib[NINDIRECT];
+ vlong atime;
+ vlong mtime;
+};
+
+enum {
+ DENTRYSIZ = NAMELEN + 4 * sizeof(ushort) + 13 + (3 + NDIRECT + NINDIRECT) * sizeof(uvlong),
+ DEPERBLK = RBLOCK / DENTRYSIZ,
+ OFFPERBLK = RBLOCK / 12,
+ REFPERBLK = RBLOCK / 3,
+};
+
+struct BufReq {
+ Dev *d;
+ uvlong off;
+ int nodata;
+ Channel *resp;
+ BufReq *next;
+};
+
+enum {
+ BWRITE = 1, /* used only for the worker */
+ BWRIM = 2, /* write immediately after putbuf */
+ BDELWRI = 4, /* write delayed */
+};
+
+struct Buf {
+ uchar op, type;
+ union {
+ uchar data[RBLOCK];
+ Superblock sb;
+ Dentry de[DEPERBLK];
+ uvlong offs[OFFPERBLK];
+ ulong refs[REFPERBLK];
+ };
+
+ /* do not use anything below (for the bufproc only) */
+ uchar busy;
+ char *error;
+ Buf *dnext, *dprev;
+ Buf *fnext, *fprev;
+ BufReq;
+ BufReq *last;
+ ulong callerpc; /* debugging */
+
+ Buf *wnext, *wprev;
+};
+
+struct ThrData {
+ Channel *resp;
+};
+
+struct Dev {
+ char *name;
+ int size;
+ Buf buf[BUFHASH+1]; /* doubly-linked list */
+ Dev *next;
+ int fd;
+ Rendez workr;
+ QLock workl;
+ Buf work;
+};
+
+extern Dev *devs;
+
+struct FLoc {
+ uvlong blk;
+ int deind;
+ Qid;
+};
+
+enum {
+ LGONE = 1,
+};
+
+struct Loc {
+ FLoc;
+ int ref, flags;
+ Loc *next, *child;
+ Loc *cnext, *cprev;
+ Loc *gnext, *gprev;
+
+ QLock ex;
+ Chan *exlock;
+ ulong lwrite;
+};
+
+enum {
+ FSNOAUTH = 1,
+ FSNOPERM = 2,
+ FSCHOWN = 4,
+};
+
+struct Fs {
+ RWLock;
+ Dev *d;
+ int flags;
+ uvlong root, fstart;
+
+ Channel *freelist;
+ Loc *rootloc, *dumprootloc;
+ QLock loctree;
+
+ User *udata;
+ int nudata;
+ RWLock udatal;
+};
+
+enum {
+ CHREAD = 1,
+ CHWRITE = 2,
+ CHRCLOSE = 4,
+ CHFDUMP = 1,
+ CHFNOLOCK = 2,
+ CHFRO = 4,
+};
+
+
+struct Chan {
+ Fs *fs;
+ Loc *loc;
+ uchar open;
+ uchar flags;
+ ushort uid;
+
+ /* dir walk */
+ uvlong dwloff;
+ uvlong dwblk;
+ int dwind;
+
+ /* workers */
+ void *freq, *lreq;
+ Chan *qnext, *qprev;
+};
+
+extern QLock chanqu;
+extern Rendez chanre;
+extern Chan readych;
+
+extern char Eio[];
+extern char Enotadir[];
+extern char Enoent[];
+extern char Einval[];
+extern char Eperm[];
+extern char Eexists[];
+extern char Elocked[];
+
+enum { /* getblk modes */
+ GBREAD = 0,
+ GBWRITE = 1,
+ GBCREATE = 2,
+ GBOVERWR = 3,
+};
+
+#define HOWMANY(a, b) (((a)+((b)-1))/(b))
+#define ROUNDUP(a, b) (HOWMANY(a,b)*(b))
--- /dev/null
+++ b/sys/src/cmd/hjfs/dev.c
@@ -1,0 +1,93 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#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;
+}
--- /dev/null
+++ b/sys/src/cmd/hjfs/dump.c
@@ -1,0 +1,169 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#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;
+}
--- /dev/null
+++ b/sys/src/cmd/hjfs/fns.h
@@ -1,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 *);
--- /dev/null
+++ b/sys/src/cmd/hjfs/fs1.c
@@ -1,0 +1,945 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <fcall.h>
+#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;
+}
--- /dev/null
+++ b/sys/src/cmd/hjfs/fs2.c
@@ -1,0 +1,742 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <fcall.h>
+#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);
+}
--- /dev/null
+++ b/sys/src/cmd/hjfs/main.c
@@ -1,0 +1,125 @@
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+#include <thread.h>
+#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);
+}
--- /dev/null
+++ b/sys/src/cmd/hjfs/mkfile
@@ -1,0 +1,21 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin
+TARG=hjfs
+OFILES=\
+ main.$O\
+ buf.$O\
+ dev.$O\
+ conv.$O\
+ fs1.$O\
+ fs2.$O\
+ auth.$O\
+ 9p.$O\
+ dump.$O\
+ cons.$O\
+
+HFILES=\
+ dat.h\
+ fns.h\
+
+</sys/src/cmd/mkone