added hjfs
This commit is contained in:
parent
ef1c186305
commit
b21b9ba89c
22 changed files with 3906 additions and 2 deletions
|
@ -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"]++}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
70
rc/bin/inst/mounthjfs
Executable 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
|
||||
}
|
|
@ -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
0
rc/bin/inst/tzsetup
Normal file → Executable file
|
@ -24,6 +24,7 @@ $objtype
|
|||
mntgen
|
||||
mount
|
||||
mv
|
||||
hjfs
|
||||
rc
|
||||
rm
|
||||
sed
|
||||
|
|
250
sys/src/cmd/hjfs/9p.c
Normal file
250
sys/src/cmd/hjfs/9p.c
Normal 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
508
sys/src/cmd/hjfs/auth.c
Normal 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
314
sys/src/cmd/hjfs/buf.c
Normal 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
238
sys/src/cmd/hjfs/cons.c
Normal 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
137
sys/src/cmd/hjfs/conv.c
Normal 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
228
sys/src/cmd/hjfs/dat.h
Normal 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
93
sys/src/cmd/hjfs/dev.c
Normal 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
169
sys/src/cmd/hjfs/dump.c
Normal 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
52
sys/src/cmd/hjfs/fns.h
Normal 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
945
sys/src/cmd/hjfs/fs1.c
Normal 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
742
sys/src/cmd/hjfs/fs2.c
Normal 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
125
sys/src/cmd/hjfs/main.c
Normal 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
21
sys/src/cmd/hjfs/mkfile
Normal 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
|
Loading…
Reference in a new issue