added hjfs

This commit is contained in:
aiju 2012-08-07 17:57:04 +02:00
parent ef1c186305
commit b21b9ba89c
22 changed files with 3906 additions and 2 deletions

View file

@ -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"]++}

View file

@ -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

View file

@ -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
}

View file

@ -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)

View file

@ -6,6 +6,8 @@
switch($fstype){
case cwfs cwfs64 cwfs64x
exec mountcwfs $*
case hjfs
exec mounthjfs $*
case *
mountfs=notdone
export mountfs

70
rc/bin/inst/mounthjfs Executable file
View file

@ -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
}

View file

@ -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
}
}

0
rc/bin/inst/tzsetup Normal file → Executable file
View file

View file

@ -24,6 +24,7 @@ $objtype
mntgen
mount
mv
hjfs
rc
rm
sed

250
sys/src/cmd/hjfs/9p.c Normal file
View file

@ -0,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);
}

508
sys/src/cmd/hjfs/auth.c Normal file
View file

@ -0,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;
}

314
sys/src/cmd/hjfs/buf.c Normal file
View file

@ -0,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);
}
}
}

238
sys/src/cmd/hjfs/cons.c Normal file
View file

@ -0,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);
}

137
sys/src/cmd/hjfs/conv.c Normal file
View file

@ -0,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);
}

228
sys/src/cmd/hjfs/dat.h Normal file
View file

@ -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<<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))

93
sys/src/cmd/hjfs/dev.c Normal file
View file

@ -0,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;
}

169
sys/src/cmd/hjfs/dump.c Normal file
View file

@ -0,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;
}

52
sys/src/cmd/hjfs/fns.h Normal file
View file

@ -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 *);

945
sys/src/cmd/hjfs/fs1.c Normal file
View file

@ -0,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;
}

742
sys/src/cmd/hjfs/fs2.c Normal file
View file

@ -0,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);
}

125
sys/src/cmd/hjfs/main.c Normal file
View file

@ -0,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);
}

21
sys/src/cmd/hjfs/mkfile Normal file
View file

@ -0,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