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
|
echo paqfs
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
if(~ $"m 011ce50d){
|
||||||
|
echo hjfs
|
||||||
|
exit
|
||||||
|
}
|
||||||
dd -if $1 -count 1 >[2]/dev/null | \
|
dd -if $1 -count 1 >[2]/dev/null | \
|
||||||
awk '
|
awk '
|
||||||
/^kfs/{fs["kfs"]++}
|
/^kfs/{fs["kfs"]++}
|
||||||
|
|
|
@ -37,7 +37,7 @@ case go
|
||||||
bootfile=9pcf
|
bootfile=9pcf
|
||||||
@{
|
@{
|
||||||
echo 'bootfile='^$bootfile
|
echo 'bootfile='^$bootfile
|
||||||
echo 'bootargs=local!'^$fs
|
echo 'bootargs=local!'^$fs^$fsflags
|
||||||
if(~ $#nvram 1)
|
if(~ $#nvram 1)
|
||||||
echo 'nvram='^$nvram
|
echo 'nvram='^$nvram
|
||||||
echo 'mouseport='^$mouseport
|
echo 'mouseport='^$mouseport
|
||||||
|
|
|
@ -14,8 +14,9 @@ case go
|
||||||
echo 'You can install the following types of file systems:'
|
echo 'You can install the following types of file systems:'
|
||||||
echo
|
echo
|
||||||
echo ' cwfs64x the cached-worm file server'
|
echo ' cwfs64x the cached-worm file server'
|
||||||
|
echo ' hjfs the new 9front file server (experimental!)'
|
||||||
echo
|
echo
|
||||||
prompt -d cwfs64x 'File system' cwfs64x
|
prompt -d cwfs64x 'File system' cwfs64x hjfs
|
||||||
fstype=$rd
|
fstype=$rd
|
||||||
export fstype
|
export fstype
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ case go
|
||||||
prompt $default 'Cwfs cache partition' $files
|
prompt $default 'Cwfs cache partition' $files
|
||||||
fs=$rd
|
fs=$rd
|
||||||
export fs
|
export fs
|
||||||
|
fsflags=
|
||||||
|
export fsflags
|
||||||
|
|
||||||
files=(`{ls /dev/sd*/fsworm* /dev/fs/fsworm* >[2]/dev/null})
|
files=(`{ls /dev/sd*/fsworm* /dev/fs/fsworm* >[2]/dev/null})
|
||||||
if(! ~ $#files 0)
|
if(! ~ $#files 0)
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
switch($fstype){
|
switch($fstype){
|
||||||
case cwfs cwfs64 cwfs64x
|
case cwfs cwfs64 cwfs64x
|
||||||
exec mountcwfs $*
|
exec mountcwfs $*
|
||||||
|
case hjfs
|
||||||
|
exec mounthjfs $*
|
||||||
case *
|
case *
|
||||||
mountfs=notdone
|
mountfs=notdone
|
||||||
export mountfs
|
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){
|
switch($fstype){
|
||||||
case cwfs cwfs64 cwfs64x
|
case cwfs cwfs64 cwfs64x
|
||||||
echo -a 9fat -a nvram -a fscache -a fsworm -a other
|
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
|
mntgen
|
||||||
mount
|
mount
|
||||||
mv
|
mv
|
||||||
|
hjfs
|
||||||
rc
|
rc
|
||||||
rm
|
rm
|
||||||
sed
|
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