Import sources from 2011-03-30 iso image

This commit is contained in:
Taru Karttunen 2011-03-30 15:46:40 +03:00
commit e5888a1ffd
7810 changed files with 2489717 additions and 0 deletions

198
sys/src/lib9p/auth.c Executable file
View file

@ -0,0 +1,198 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
typedef struct Afid Afid;
struct Afid
{
AuthRpc *rpc;
char *uname;
char *aname;
int authok;
int afd;
};
static uvlong authgen = 1ULL<<63;
void
auth9p(Req *r)
{
char *spec;
Afid *afid;
afid = emalloc9p(sizeof(Afid));
afid->afd = open("/mnt/factotum/rpc", ORDWR);
if(afid->afd < 0)
goto error;
if((afid->rpc = auth_allocrpc(afid->afd)) == nil)
goto error;
if(r->ifcall.uname[0] == 0)
goto error;
afid->uname = estrdup9p(r->ifcall.uname);
afid->aname = estrdup9p(r->ifcall.aname);
spec = r->srv->keyspec;
if(spec == nil)
spec = "proto=p9any role=server";
if(auth_rpc(afid->rpc, "start", spec, strlen(spec)) != ARok)
goto error;
r->afid->qid.type = QTAUTH;
r->afid->qid.path = ++authgen;
r->afid->qid.vers = 0;
r->afid->omode = ORDWR;
r->ofcall.qid = r->afid->qid;
r->afid->aux = afid;
respond(r, nil);
return;
error:
if(afid->rpc)
auth_freerpc(afid->rpc);
if(afid->uname)
free(afid->uname);
if(afid->aname)
free(afid->aname);
if(afid->afd >= 0)
close(afid->afd);
free(afid);
responderror(r);
}
static int
_authread(Afid *afid, void *data, int count)
{
AuthInfo *ai;
switch(auth_rpc(afid->rpc, "read", nil, 0)){
case ARdone:
ai = auth_getinfo(afid->rpc);
if(ai == nil)
return -1;
auth_freeAI(ai);
if(chatty9p)
fprint(2, "authenticate %s/%s: ok\n", afid->uname, afid->aname);
afid->authok = 1;
return 0;
case ARok:
if(count < afid->rpc->narg){
werrstr("authread count too small");
return -1;
}
count = afid->rpc->narg;
memmove(data, afid->rpc->arg, count);
return count;
case ARphase:
default:
werrstr("authrpc botch");
return -1;
}
}
void
authread(Req *r)
{
int n;
Afid *afid;
Fid *fid;
fid = r->fid;
afid = fid->aux;
if(afid == nil || r->fid->qid.type != QTAUTH){
respond(r, "not an auth fid");
return;
}
n = _authread(afid, r->ofcall.data, r->ifcall.count);
if(n < 0){
responderror(r);
return;
}
r->ofcall.count = n;
respond(r, nil);
}
void
authwrite(Req *r)
{
Afid *afid;
Fid *fid;
fid = r->fid;
afid = fid->aux;
if(afid == nil || r->fid->qid.type != QTAUTH){
respond(r, "not an auth fid");
return;
}
if(auth_rpc(afid->rpc, "write", r->ifcall.data, r->ifcall.count) != ARok){
responderror(r);
return;
}
r->ofcall.count = r->ifcall.count;
respond(r, nil);
}
void
authdestroy(Fid *fid)
{
Afid *afid;
if((fid->qid.type & QTAUTH) && (afid = fid->aux) != nil){
if(afid->rpc)
auth_freerpc(afid->rpc);
close(afid->afd);
free(afid->uname);
free(afid->aname);
free(afid);
fid->aux = nil;
}
}
int
authattach(Req *r)
{
Afid *afid;
char buf[ERRMAX];
if(r->afid == nil){
respond(r, "not authenticated");
return -1;
}
afid = r->afid->aux;
if((r->afid->qid.type&QTAUTH) == 0 || afid == nil){
respond(r, "not an auth fid");
return -1;
}
if(!afid->authok){
if(_authread(afid, buf, 0) < 0){
responderror(r);
return -1;
}
}
if(strcmp(afid->uname, r->ifcall.uname) != 0){
snprint(buf, sizeof buf, "auth uname mismatch: %s vs %s",
afid->uname, r->ifcall.uname);
respond(r, buf);
return -1;
}
if(strcmp(afid->aname, r->ifcall.aname) != 0){
snprint(buf, sizeof buf, "auth aname mismatch: %s vs %s",
afid->aname, r->ifcall.aname);
respond(r, buf);
return -1;
}
return 0;
}

40
sys/src/lib9p/dirread.c Executable file
View file

@ -0,0 +1,40 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
void
dirread9p(Req *r, Dirgen *gen, void *aux)
{
int start;
uchar *p, *ep;
uint rv;
Dir d;
if(r->ifcall.offset == 0)
start = 0;
else
start = r->fid->dirindex;
p = (uchar*)r->ofcall.data;
ep = p+r->ifcall.count;
while(p < ep){
memset(&d, 0, sizeof d);
if((*gen)(start, &d, aux) < 0)
break;
rv = convD2M(&d, p, ep-p);
free(d.name);
free(d.muid);
free(d.uid);
free(d.gid);
if(rv <= BIT16SZ)
break;
p += rv;
start++;
}
r->fid->dirindex = start;
r->ofcall.count = p - (uchar*)r->ofcall.data;
}

82
sys/src/lib9p/fid.c Executable file
View file

@ -0,0 +1,82 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include "9p.h"
static void
incfidref(void *v)
{
Fid *f;
f = v;
if(f)
incref(&f->ref);
}
Fidpool*
allocfidpool(void (*destroy)(Fid*))
{
Fidpool *f;
f = emalloc9p(sizeof *f);
f->map = allocmap(incfidref);
f->destroy = destroy;
return f;
}
void
freefidpool(Fidpool *p)
{
freemap(p->map, (void(*)(void*))p->destroy);
free(p);
}
Fid*
allocfid(Fidpool *pool, ulong fid)
{
Fid *f;
f = emalloc9p(sizeof *f);
f->fid = fid;
f->omode = -1;
f->pool = pool;
incfidref(f);
incfidref(f);
if(caninsertkey(pool->map, fid, f) == 0){
closefid(f);
closefid(f);
return nil;
}
return f;
}
Fid*
lookupfid(Fidpool *pool, ulong fid)
{
return lookupkey(pool->map, fid);
}
void
closefid(Fid *f)
{
if(decref(&f->ref) == 0) {
if(f->rdir)
closedirfile(f->rdir);
if(f->pool->destroy)
f->pool->destroy(f);
if(f->file)
closefile(f->file);
free(f->uid);
free(f);
}
}
Fid*
removefid(Fidpool *pool, ulong fid)
{
return deletekey(pool->map, fid);
}

415
sys/src/lib9p/file.c Executable file
View file

@ -0,0 +1,415 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
/*
* To avoid deadlock, the following rules must be followed.
* Always lock child then parent, never parent then child.
* If holding the free file lock, do not lock any Files.
*/
struct Filelist
{
File *f;
Filelist *link;
};
struct Readdir
{
File *dir;
Filelist *fl;
};
static QLock filelk;
static File *freefilelist;
static File*
allocfile(void)
{
int i, a;
File *f;
enum { N = 16 };
qlock(&filelk);
if(freefilelist == nil){
f = emalloc9p(N*sizeof(*f));
for(i=0; i<N-1; i++)
f[i].aux = &f[i+1];
f[N-1].aux = nil;
f[0].allocd = 1;
freefilelist = f;
}
f = freefilelist;
freefilelist = f->aux;
qunlock(&filelk);
a = f->allocd;
memset(f, 0, sizeof *f);
f->allocd = a;
return f;
}
static void
freefile(File *f)
{
Filelist *fl, *flnext;
for(fl=f->filelist; fl; fl=flnext){
flnext = fl->link;
assert(fl->f == nil);
free(fl);
}
free(f->name);
free(f->uid);
free(f->gid);
free(f->muid);
qlock(&filelk);
assert(f->ref == 0);
f->aux = freefilelist;
freefilelist = f;
qunlock(&filelk);
}
static void
cleanfilelist(File *f)
{
Filelist **l;
Filelist *fl;
/*
* can't delete filelist structures while there
* are open readers of this directory, because
* they might have references to the structures.
* instead, just leave the empty refs in the list
* until there is no activity and then clean up.
*/
if(f->readers.ref != 0)
return;
if(f->nxchild == 0)
return;
/*
* no dir readers, file is locked, and
* there are empty entries in the file list.
* clean them out.
*/
for(l=&f->filelist; fl=*l; ){
if(fl->f == nil){
*l = (*l)->link;
free(fl);
}else
l = &(*l)->link;
}
f->nxchild = 0;
}
void
closefile(File *f)
{
if(decref(f) == 0){
f->tree->destroy(f);
freefile(f);
}
}
static void
nop(File*)
{
}
int
removefile(File *f)
{
File *fp;
Filelist *fl;
fp = f->parent;
if(fp == nil){
werrstr("no parent");
closefile(f);
return -1;
}
if(fp == f){
werrstr("cannot remove root");
closefile(f);
return -1;
}
wlock(f);
wlock(fp);
if(f->nchild != 0){
werrstr("has children");
wunlock(fp);
wunlock(f);
closefile(f);
return -1;
}
if(f->parent != fp){
werrstr("parent changed underfoot");
wunlock(fp);
wunlock(f);
closefile(f);
return -1;
}
for(fl=fp->filelist; fl; fl=fl->link)
if(fl->f == f)
break;
assert(fl != nil && fl->f == f);
fl->f = nil;
fp->nchild--;
fp->nxchild++;
f->parent = nil;
wunlock(f);
cleanfilelist(fp);
wunlock(fp);
closefile(fp); /* reference from child */
closefile(f); /* reference from tree */
closefile(f);
return 0;
}
File*
createfile(File *fp, char *name, char *uid, ulong perm, void *aux)
{
File *f;
Filelist **l, *fl;
Tree *t;
if((fp->qid.type&QTDIR) == 0){
werrstr("create in non-directory");
return nil;
}
wlock(fp);
/*
* We might encounter blank spots along the
* way due to deleted files that have not yet
* been flushed from the file list. Don't reuse
* those - some apps (e.g., omero) depend on
* the file order reflecting creation order.
* Always create at the end of the list.
*/
for(l=&fp->filelist; fl=*l; l=&fl->link){
if(fl->f && strcmp(fl->f->name, name) == 0){
wunlock(fp);
werrstr("file already exists");
return nil;
}
}
fl = emalloc9p(sizeof *fl);
*l = fl;
f = allocfile();
f->name = estrdup9p(name);
f->uid = estrdup9p(uid ? uid : fp->uid);
f->gid = estrdup9p(fp->gid);
f->muid = estrdup9p(uid ? uid : "unknown");
f->aux = aux;
f->mode = perm;
t = fp->tree;
lock(&t->genlock);
f->qid.path = t->qidgen++;
unlock(&t->genlock);
if(perm & DMDIR)
f->qid.type |= QTDIR;
if(perm & DMAPPEND)
f->qid.type |= QTAPPEND;
if(perm & DMEXCL)
f->qid.type |= QTEXCL;
f->mode = perm;
f->atime = f->mtime = time(0);
f->length = 0;
f->parent = fp;
incref(fp);
f->tree = fp->tree;
incref(f); /* being returned */
incref(f); /* for the tree */
fl->f = f;
fp->nchild++;
wunlock(fp);
return f;
}
static File*
walkfile1(File *dir, char *elem)
{
File *fp;
Filelist *fl;
rlock(dir);
if(strcmp(elem, "..") == 0){
fp = dir->parent;
incref(fp);
runlock(dir);
closefile(dir);
return fp;
}
fp = nil;
for(fl=dir->filelist; fl; fl=fl->link)
if(fl->f && strcmp(fl->f->name, elem)==0){
fp = fl->f;
incref(fp);
break;
}
runlock(dir);
closefile(dir);
return fp;
}
File*
walkfile(File *f, char *path)
{
char *os, *s, *nexts;
if(strchr(path, '/') == nil)
return walkfile1(f, path); /* avoid malloc */
os = s = estrdup9p(path);
for(; *s; s=nexts){
if(nexts = strchr(s, '/'))
*nexts++ = '\0';
else
nexts = s+strlen(s);
f = walkfile1(f, s);
if(f == nil)
break;
}
free(os);
return f;
}
Tree*
alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*))
{
char *muid;
Tree *t;
File *f;
t = emalloc9p(sizeof *t);
f = allocfile();
f->name = estrdup9p("/");
if(uid == nil){
uid = getuser();
if(uid == nil)
uid = "none";
}
uid = estrdup9p(uid);
if(gid == nil)
gid = estrdup9p(uid);
else
gid = estrdup9p(gid);
muid = estrdup9p(uid);
f->qid = (Qid){0, 0, QTDIR};
f->length = 0;
f->atime = f->mtime = time(0);
f->mode = DMDIR | mode;
f->tree = t;
f->parent = f;
f->uid = uid;
f->gid = gid;
f->muid = muid;
incref(f);
t->root = f;
t->qidgen = 0;
t->dirqidgen = 1;
if(destroy == nil)
destroy = nop;
t->destroy = destroy;
return t;
}
static void
_freefiles(File *f)
{
Filelist *fl, *flnext;
for(fl=f->filelist; fl; fl=flnext){
flnext = fl->link;
_freefiles(fl->f);
free(fl);
}
f->tree->destroy(f);
freefile(f);
}
void
freetree(Tree *t)
{
_freefiles(t->root);
free(t);
}
Readdir*
opendirfile(File *dir)
{
Readdir *r;
rlock(dir);
if((dir->mode & DMDIR)==0){
runlock(dir);
return nil;
}
r = emalloc9p(sizeof(*r));
/*
* This reference won't go away while we're
* using it because file list entries are not freed
* until the directory is removed and all refs to
* it (our fid is one!) have gone away.
*/
r->fl = dir->filelist;
r->dir = dir;
incref(&dir->readers);
runlock(dir);
return r;
}
long
readdirfile(Readdir *r, uchar *buf, long n)
{
long x, m;
Filelist *fl;
for(fl=r->fl, m=0; fl && m+2<=n; fl=fl->link, m+=x){
if(fl->f == nil)
x = 0;
else if((x=convD2M(fl->f, buf+m, n-m)) <= BIT16SZ)
break;
}
r->fl = fl;
return m;
}
void
closedirfile(Readdir *r)
{
if(decref(&r->dir->readers) == 0){
wlock(r->dir);
cleanfilelist(r->dir);
wunlock(r->dir);
}
free(r);
}

29
sys/src/lib9p/ftest.c Executable file
View file

@ -0,0 +1,29 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include "9p.h"
void
main(void)
{
Tree *t;
File *hello, *goodbye, *world;
t = mktree();
hello = fcreate(t->root, "hello", CHDIR|0777);
assert(hello != nil);
goodbye = fcreate(t->root, "goodbye", CHDIR|0777);
assert(goodbye != nil);
world = fcreate(hello, "world", 0666);
assert(world != nil);
world = fcreate(goodbye, "world", 0666);
assert(world != nil);
fdump(t->root, 0);
fremove(world);
fdump(t->root, 0);
}

165
sys/src/lib9p/intmap.c Executable file
View file

@ -0,0 +1,165 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
enum {
NHASH = 128
};
typedef struct Intlist Intlist;
struct Intlist
{
ulong id;
void* aux;
Intlist* link;
};
struct Intmap
{
RWLock;
Intlist* hash[NHASH];
void (*inc)(void*);
};
static ulong
hashid(ulong id)
{
return id%NHASH;
}
static void
nop(void*)
{
}
Intmap*
allocmap(void (*inc)(void*))
{
Intmap *m;
m = emalloc9p(sizeof(*m));
if(inc == nil)
inc = nop;
m->inc = inc;
return m;
}
void
freemap(Intmap *map, void (*destroy)(void*))
{
int i;
Intlist *p, *nlink;
if(destroy == nil)
destroy = nop;
for(i=0; i<NHASH; i++){
for(p=map->hash[i]; p; p=nlink){
nlink = p->link;
destroy(p->aux);
free(p);
}
}
free(map);
}
static Intlist**
llookup(Intmap *map, ulong id)
{
Intlist **lf;
for(lf=&map->hash[hashid(id)]; *lf; lf=&(*lf)->link)
if((*lf)->id == id)
break;
return lf;
}
/*
* The RWlock is used as expected except that we allow
* inc() to be called while holding it. This is because we're
* locking changes to the tree structure, not to the references.
* Inc() is expected to have its own locking.
*/
void*
lookupkey(Intmap *map, ulong id)
{
Intlist *f;
void *v;
rlock(map);
if(f = *llookup(map, id)){
v = f->aux;
map->inc(v);
}else
v = nil;
runlock(map);
return v;
}
void*
insertkey(Intmap *map, ulong id, void *v)
{
Intlist *f;
void *ov;
ulong h;
wlock(map);
if(f = *llookup(map, id)){
/* no decrement for ov because we're returning it */
ov = f->aux;
f->aux = v;
}else{
f = emalloc9p(sizeof(*f));
f->id = id;
f->aux = v;
h = hashid(id);
f->link = map->hash[h];
map->hash[h] = f;
ov = nil;
}
wunlock(map);
return ov;
}
int
caninsertkey(Intmap *map, ulong id, void *v)
{
Intlist *f;
int rv;
ulong h;
wlock(map);
if(*llookup(map, id))
rv = 0;
else{
f = emalloc9p(sizeof *f);
f->id = id;
f->aux = v;
h = hashid(id);
f->link = map->hash[h];
map->hash[h] = f;
rv = 1;
}
wunlock(map);
return rv;
}
void*
deletekey(Intmap *map, ulong id)
{
Intlist **lf, *f;
void *ov;
wlock(map);
if(f = *(lf = llookup(map, id))){
ov = f->aux;
*lf = f->link;
free(f);
}else
ov = nil;
wunlock(map);
return ov;
}

103
sys/src/lib9p/listen.c Executable file
View file

@ -0,0 +1,103 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
static void listenproc(void*);
static void srvproc(void*);
static char *getremotesys(char*);
void
_listensrv(Srv *os, char *addr)
{
Srv *s;
if(_forker == nil)
sysfatal("no forker");
s = emalloc9p(sizeof *s);
*s = *os;
s->addr = estrdup9p(addr);
_forker(listenproc, s, 0);
}
static void
listenproc(void *v)
{
char ndir[NETPATHLEN], dir[NETPATHLEN];
int ctl, data, nctl;
Srv *os, *s;
os = v;
ctl = announce(os->addr, dir);
if(ctl < 0){
fprint(2, "%s: announce %s: %r", argv0, os->addr);
return;
}
for(;;){
nctl = listen(dir, ndir);
if(nctl < 0){
fprint(2, "%s: listen %s: %r", argv0, os->addr);
break;
}
data = accept(ctl, ndir);
if(data < 0){
fprint(2, "%s: accept %s: %r\n", argv0, ndir);
continue;
}
s = emalloc9p(sizeof *s);
*s = *os;
s->addr = getremotesys(ndir);
s->infd = s->outfd = data;
s->fpool = nil;
s->rpool = nil;
s->rbuf = nil;
s->wbuf = nil;
_forker(srvproc, s, 0);
}
free(os->addr);
free(os);
}
static void
srvproc(void *v)
{
int data;
Srv *s;
s = v;
data = s->infd;
srv(s);
close(data);
free(s->addr);
free(s);
}
static char*
getremotesys(char *ndir)
{
char buf[128], *serv, *sys;
int fd, n;
snprint(buf, sizeof buf, "%s/remote", ndir);
sys = nil;
fd = open(buf, OREAD);
if(fd >= 0){
n = read(fd, buf, sizeof(buf)-1);
if(n>0){
buf[n-1] = 0;
serv = strchr(buf, '!');
if(serv)
*serv = 0;
sys = estrdup9p(buf);
}
close(fd);
}
if(sys == nil)
sys = estrdup9p("unknown");
return sys;
}

49
sys/src/lib9p/mem.c Executable file
View file

@ -0,0 +1,49 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include "9p.h"
void*
emalloc9p(ulong sz)
{
void *v;
if((v = malloc(sz)) == nil) {
fprint(2, "out of memory allocating %lud\n", sz);
exits("mem");
}
memset(v, 0, sz);
setmalloctag(v, getcallerpc(&sz));
return v;
}
void*
erealloc9p(void *v, ulong sz)
{
void *nv;
if((nv = realloc(v, sz)) == nil) {
fprint(2, "out of memory allocating %lud\n", sz);
exits("mem");
}
if(v == nil)
setmalloctag(nv, getcallerpc(&v));
setrealloctag(nv, getcallerpc(&v));
return nv;
}
char*
estrdup9p(char *s)
{
char *t;
if((t = strdup(s)) == nil) {
fprint(2, "out of memory in strdup(%.10s)\n", s);
exits("mem");
}
setmalloctag(t, getcallerpc(&s));
return t;
}

32
sys/src/lib9p/mkfile Executable file
View file

@ -0,0 +1,32 @@
</$objtype/mkfile
LIB=/$objtype/lib/lib9p.a
OFILES=\
auth.$O\
dirread.$O\
fid.$O\
file.$O\
intmap.$O\
listen.$O\
mem.$O\
req.$O\
parse.$O\
post.$O\
rfork.$O\
srv.$O\
thread.$O\
uid.$O\
util.$O\
HFILES=/sys/include/9p.h
UPDATE=\
mkfile\
$HFILES\
${OFILES:%.$O=%.c}\
${LIB:/$objtype/%=/386/%}\
</sys/src/cmd/mksyslib
$O.ramfs: ramfs.$O $LIB
$LD -o $target $prereq

116
sys/src/lib9p/parse.c Executable file
View file

@ -0,0 +1,116 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
/*
* Generous estimate of number of fields, including terminal nil pointer
*/
static int
ncmdfield(char *p, int n)
{
int white, nwhite;
char *ep;
int nf;
if(p == nil)
return 1;
nf = 0;
ep = p+n;
white = 1; /* first text will start field */
while(p < ep){
nwhite = (strchr(" \t\r\n", *p++ & 0xFF) != 0); /* UTF is irrelevant */
if(white && !nwhite) /* beginning of field */
nf++;
white = nwhite;
}
return nf+1; /* +1 for nil */
}
/*
* parse a command written to a device
*/
Cmdbuf*
parsecmd(char *p, int n)
{
Cmdbuf *cb;
int nf;
char *sp;
nf = ncmdfield(p, n);
/* allocate Cmdbuf plus string pointers plus copy of string including \0 */
sp = emalloc9p(sizeof(*cb) + nf * sizeof(char*) + n + 1);
cb = (Cmdbuf*)sp;
cb->f = (char**)(&cb[1]);
cb->buf = (char*)(&cb->f[nf]);
memmove(cb->buf, p, n);
/* dump new line and null terminate */
if(n > 0 && cb->buf[n-1] == '\n')
n--;
cb->buf[n] = '\0';
cb->nf = tokenize(cb->buf, cb->f, nf-1);
cb->f[cb->nf] = nil;
return cb;
}
/*
* Reconstruct original message, for error diagnostic
*/
void
respondcmderror(Req *r, Cmdbuf *cb, char *fmt, ...)
{
int i;
va_list arg;
char *p, *e;
char err[ERRMAX];
e = err+ERRMAX-10;
va_start(arg, fmt);
p = vseprint(err, e, fmt, arg);
va_end(arg);
p = seprint(p, e, ": \"");
quotefmtinstall(); /* just in case */
for(i=0; i<cb->nf; i++){
if(i > 0)
p = seprint(p, e, " ");
p = seprint(p, e, "%q", cb->f[i]);
}
strcpy(p, "\"");
respond(r, err);
}
/*
* Look up entry in table
*/
Cmdtab*
lookupcmd(Cmdbuf *cb, Cmdtab *ctab, int nctab)
{
int i;
Cmdtab *ct;
if(cb->nf == 0){
werrstr("empty control message");
return nil;
}
for(ct = ctab, i=0; i<nctab; i++, ct++){
if(strcmp(ct->cmd, "*") !=0) /* wildcard always matches */
if(strcmp(ct->cmd, cb->f[0]) != 0)
continue;
if(ct->narg != 0 && ct->narg != cb->nf){
werrstr("bad # args to command");
return nil;
}
return ct;
}
werrstr("unknown control message");
return nil;
}

71
sys/src/lib9p/post.c Executable file
View file

@ -0,0 +1,71 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <auth.h>
static void postproc(void*);
void
_postmountsrv(Srv *s, char *name, char *mtpt, int flag)
{
int fd[2];
if(!s->nopipe){
if(pipe(fd) < 0)
sysfatal("pipe: %r");
s->infd = s->outfd = fd[1];
s->srvfd = fd[0];
}
if(name)
if(postfd(name, s->srvfd) < 0)
sysfatal("postfd %s: %r", name);
if(_forker == nil)
sysfatal("no forker");
_forker(postproc, s, RFNAMEG);
/*
* Normally the server is posting as the last thing it does
* before exiting, so the correct thing to do is drop into
* a different fd space and close the 9P server half of the
* pipe before trying to mount the kernel half. This way,
* if the file server dies, we don't have a ref to the 9P server
* half of the pipe. Then killing the other procs will drop
* all the refs on the 9P server half, and the mount will fail.
* Otherwise the mount hangs forever.
*
* Libthread in general and acme win in particular make
* it hard to make this fd bookkeeping work out properly,
* so leaveinfdopen is a flag that win sets to opt out of this
* safety net.
*/
if(!s->leavefdsopen){
rfork(RFFDG);
rendezvous(0, 0);
close(s->infd);
if(s->infd != s->outfd)
close(s->outfd);
}
if(mtpt){
if(amount(s->srvfd, mtpt, flag, "") == -1)
sysfatal("mount %s: %r", mtpt);
}else
close(s->srvfd);
}
static void
postproc(void *v)
{
Srv *s;
s = v;
if(!s->leavefdsopen){
rfork(RFNOTEG);
rendezvous(0, 0);
close(s->srvfd);
}
srv(s);
}

170
sys/src/lib9p/ramfs.c Executable file
View file

@ -0,0 +1,170 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
static char Ebad[] = "something bad happened";
static char Enomem[] = "no memory";
typedef struct Ramfile Ramfile;
struct Ramfile {
char *data;
int ndata;
};
void
fsread(Req *r)
{
Ramfile *rf;
vlong offset;
long count;
rf = r->fid->file->aux;
offset = r->ifcall.offset;
count = r->ifcall.count;
//print("read %ld %lld\n", *count, offset);
if(offset >= rf->ndata){
r->ofcall.count = 0;
respond(r, nil);
return;
}
if(offset+count >= rf->ndata)
count = rf->ndata - offset;
memmove(r->ofcall.data, rf->data+offset, count);
r->ofcall.count = count;
respond(r, nil);
}
void
fswrite(Req *r)
{
void *v;
Ramfile *rf;
vlong offset;
long count;
rf = r->fid->file->aux;
offset = r->ifcall.offset;
count = r->ifcall.count;
if(offset+count >= rf->ndata){
v = realloc(rf->data, offset+count);
if(v == nil){
respond(r, Enomem);
return;
}
rf->data = v;
rf->ndata = offset+count;
r->fid->file->length = rf->ndata;
}
memmove(rf->data+offset, r->ifcall.data, count);
r->ofcall.count = count;
respond(r, nil);
}
void
fscreate(Req *r)
{
Ramfile *rf;
File *f;
if(f = createfile(r->fid->file, r->ifcall.name, r->fid->uid, r->ifcall.perm, nil)){
rf = emalloc9p(sizeof *rf);
f->aux = rf;
r->fid->file = f;
r->ofcall.qid = f->qid;
respond(r, nil);
return;
}
respond(r, Ebad);
}
void
fsopen(Req *r)
{
Ramfile *rf;
rf = r->fid->file->aux;
if(rf && (r->ifcall.mode&OTRUNC)){
rf->ndata = 0;
r->fid->file->length = 0;
}
respond(r, nil);
}
void
fsdestroyfile(File *f)
{
Ramfile *rf;
//fprint(2, "clunk\n");
rf = f->aux;
if(rf){
free(rf->data);
free(rf);
}
}
Srv fs = {
.open= fsopen,
.read= fsread,
.write= fswrite,
.create= fscreate,
};
void
usage(void)
{
fprint(2, "usage: ramfs [-D] [-s srvname] [-m mtpt]\n");
exits("usage");
}
void
main(int argc, char **argv)
{
char *addr = nil;
char *srvname = nil;
char *mtpt = nil;
Qid q;
fs.tree = alloctree(nil, nil, DMDIR|0777, fsdestroyfile);
q = fs.tree->root->qid;
ARGBEGIN{
case 'D':
chatty9p++;
break;
case 'a':
addr = EARGF(usage());
break;
case 's':
srvname = EARGF(usage());
break;
case 'm':
mtpt = EARGF(usage());
break;
default:
usage();
}ARGEND;
if(argc)
usage();
if(chatty9p)
fprint(2, "ramsrv.nopipe %d srvname %s mtpt %s\n", fs.nopipe, srvname, mtpt);
if(addr == nil && srvname == nil && mtpt == nil)
sysfatal("must specify -a, -s, or -m option");
if(addr)
listensrv(&fs, addr);
if(srvname || mtpt)
postmountsrv(&fs, srvname, mtpt, MREPL|MCREATE);
exits(0);
}

113
sys/src/lib9p/req.c Executable file
View file

@ -0,0 +1,113 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
static void
increqref(void *v)
{
Req *r;
r = v;
if(r){
if(chatty9p > 1)
fprint(2, "increfreq %p %ld\n", r, r->ref.ref);
incref(&r->ref);
}
}
Reqpool*
allocreqpool(void (*destroy)(Req*))
{
Reqpool *f;
f = emalloc9p(sizeof *f);
f->map = allocmap(increqref);
f->destroy = destroy;
return f;
}
void
freereqpool(Reqpool *p)
{
freemap(p->map, (void(*)(void*))p->destroy);
free(p);
}
Req*
allocreq(Reqpool *pool, ulong tag)
{
Req *r;
r = emalloc9p(sizeof *r);
r->tag = tag;
r->pool = pool;
increqref(r);
increqref(r);
if(caninsertkey(pool->map, tag, r) == 0){
closereq(r);
closereq(r);
return nil;
}
return r;
}
Req*
lookupreq(Reqpool *pool, ulong tag)
{
if(chatty9p > 1)
fprint(2, "lookupreq %lud\n", tag);
return lookupkey(pool->map, tag);
}
void
closereq(Req *r)
{
int i;
if(r == nil)
return;
if(chatty9p > 1)
fprint(2, "closereq %p %ld\n", r, r->ref.ref);
if(decref(&r->ref) == 0){
if(r->fid)
closefid(r->fid);
if(r->newfid)
closefid(r->newfid);
if(r->afid)
closefid(r->afid);
if(r->oldreq)
closereq(r->oldreq);
for(i=0; i<r->nflush; i++)
respond(r->flush[i], nil);
free(r->flush);
switch(r->ifcall.type){
case Tstat:
free(r->ofcall.stat);
free(r->d.name);
free(r->d.uid);
free(r->d.gid);
free(r->d.muid);
break;
}
if(r->pool->destroy)
r->pool->destroy(r);
free(r->buf);
free(r->rbuf);
free(r);
}
}
Req*
removereq(Reqpool *pool, ulong tag)
{
if(chatty9p > 1)
fprint(2, "removereq %lud\n", tag);
return deletekey(pool->map, tag);
}

34
sys/src/lib9p/rfork.c Executable file
View file

@ -0,0 +1,34 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
static void
rforker(void (*fn)(void*), void *arg, int flag)
{
switch(rfork(RFPROC|RFMEM|RFNOWAIT|flag)){
case -1:
sysfatal("rfork: %r");
default:
return;
case 0:
fn(arg);
_exits(0);
}
}
void
listensrv(Srv *s, char *addr)
{
_forker = rforker;
_listensrv(s, addr);
}
void
postmountsrv(Srv *s, char *name, char *mtpt, int flag)
{
_forker = rforker;
_postmountsrv(s, name, mtpt, flag);
}

855
sys/src/lib9p/srv.c Executable file
View file

@ -0,0 +1,855 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
void (*_forker)(void(*)(void*), void*, int);
static char Ebadattach[] = "unknown specifier in attach";
static char Ebadoffset[] = "bad offset";
static char Ebadcount[] = "bad count";
static char Ebotch[] = "9P protocol botch";
static char Ecreatenondir[] = "create in non-directory";
static char Edupfid[] = "duplicate fid";
static char Eduptag[] = "duplicate tag";
static char Eisdir[] = "is a directory";
static char Enocreate[] = "create prohibited";
static char Enomem[] = "out of memory";
static char Enoremove[] = "remove prohibited";
static char Enostat[] = "stat prohibited";
static char Enotfound[] = "file not found";
static char Enowrite[] = "write prohibited";
static char Enowstat[] = "wstat prohibited";
static char Eperm[] = "permission denied";
static char Eunknownfid[] = "unknown fid";
static char Ebaddir[] = "bad directory in wstat";
static char Ewalknodir[] = "walk in non-directory";
static void
setfcallerror(Fcall *f, char *err)
{
f->ename = err;
f->type = Rerror;
}
static void
changemsize(Srv *srv, int msize)
{
if(srv->rbuf && srv->wbuf && srv->msize == msize)
return;
qlock(&srv->rlock);
qlock(&srv->wlock);
srv->msize = msize;
free(srv->rbuf);
free(srv->wbuf);
srv->rbuf = emalloc9p(msize);
srv->wbuf = emalloc9p(msize);
qunlock(&srv->rlock);
qunlock(&srv->wlock);
}
static Req*
getreq(Srv *s)
{
long n;
uchar *buf;
Fcall f;
Req *r;
qlock(&s->rlock);
if((n = read9pmsg(s->infd, s->rbuf, s->msize)) <= 0){
qunlock(&s->rlock);
return nil;
}
buf = emalloc9p(n);
memmove(buf, s->rbuf, n);
qunlock(&s->rlock);
if(convM2S(buf, n, &f) != n){
free(buf);
return nil;
}
if((r=allocreq(s->rpool, f.tag)) == nil){ /* duplicate tag: cons up a fake Req */
r = emalloc9p(sizeof *r);
incref(&r->ref);
r->tag = f.tag;
r->ifcall = f;
r->error = Eduptag;
r->buf = buf;
r->responded = 0;
r->type = 0;
r->srv = s;
r->pool = nil;
if(chatty9p)
fprint(2, "<-%d- %F: dup tag\n", s->infd, &f);
return r;
}
r->srv = s;
r->responded = 0;
r->buf = buf;
r->ifcall = f;
memset(&r->ofcall, 0, sizeof r->ofcall);
r->type = r->ifcall.type;
if(chatty9p)
if(r->error)
fprint(2, "<-%d- %F: %s\n", s->infd, &r->ifcall, r->error);
else
fprint(2, "<-%d- %F\n", s->infd, &r->ifcall);
return r;
}
static void
filewalk(Req *r)
{
int i;
File *f;
f = r->fid->file;
assert(f != nil);
incref(f);
for(i=0; i<r->ifcall.nwname; i++)
if(f = walkfile(f, r->ifcall.wname[i]))
r->ofcall.wqid[i] = f->qid;
else
break;
r->ofcall.nwqid = i;
if(f){
r->newfid->file = f;
r->newfid->qid = r->newfid->file->qid;
}
respond(r, nil);
}
void
walkandclone(Req *r, char *(*walk1)(Fid*, char*, void*), char *(*clone)(Fid*, Fid*, void*), void *arg)
{
int i;
char *e;
if(r->fid == r->newfid && r->ifcall.nwname > 1){
respond(r, "lib9p: unused documented feature not implemented");
return;
}
if(r->fid != r->newfid){
r->newfid->qid = r->fid->qid;
if(clone && (e = clone(r->fid, r->newfid, arg))){
respond(r, e);
return;
}
}
e = nil;
for(i=0; i<r->ifcall.nwname; i++){
if(e = walk1(r->newfid, r->ifcall.wname[i], arg))
break;
r->ofcall.wqid[i] = r->newfid->qid;
}
r->ofcall.nwqid = i;
if(e && i==0)
respond(r, e);
else
respond(r, nil);
}
static void
sversion(Srv*, Req *r)
{
if(strncmp(r->ifcall.version, "9P", 2) != 0){
r->ofcall.version = "unknown";
respond(r, nil);
return;
}
r->ofcall.version = "9P2000";
r->ofcall.msize = r->ifcall.msize;
respond(r, nil);
}
static void
rversion(Req *r, char *error)
{
assert(error == nil);
changemsize(r->srv, r->ofcall.msize);
}
static void
sauth(Srv *srv, Req *r)
{
char e[ERRMAX];
if((r->afid = allocfid(srv->fpool, r->ifcall.afid)) == nil){
respond(r, Edupfid);
return;
}
if(srv->auth)
srv->auth(r);
else{
snprint(e, sizeof e, "%s: authentication not required", argv0);
respond(r, e);
}
}
static void
rauth(Req *r, char *error)
{
if(error && r->afid)
closefid(removefid(r->srv->fpool, r->afid->fid));
}
static void
sattach(Srv *srv, Req *r)
{
if((r->fid = allocfid(srv->fpool, r->ifcall.fid)) == nil){
respond(r, Edupfid);
return;
}
r->afid = nil;
if(r->ifcall.afid != NOFID && (r->afid = lookupfid(srv->fpool, r->ifcall.afid)) == nil){
respond(r, Eunknownfid);
return;
}
r->fid->uid = estrdup9p(r->ifcall.uname);
if(srv->tree){
r->fid->file = srv->tree->root;
incref(r->fid->file);
r->ofcall.qid = r->fid->file->qid;
r->fid->qid = r->ofcall.qid;
}
if(srv->attach)
srv->attach(r);
else
respond(r, nil);
return;
}
static void
rattach(Req *r, char *error)
{
if(error && r->fid)
closefid(removefid(r->srv->fpool, r->fid->fid));
}
static void
sflush(Srv *srv, Req *r)
{
r->oldreq = lookupreq(srv->rpool, r->ifcall.oldtag);
if(r->oldreq == nil || r->oldreq == r)
respond(r, nil);
else if(srv->flush)
srv->flush(r);
else
respond(r, nil);
}
static int
rflush(Req *r, char *error)
{
Req *or;
assert(error == nil);
or = r->oldreq;
if(or){
qlock(&or->lk);
if(or->responded == 0){
or->flush = erealloc9p(or->flush, (or->nflush+1)*sizeof(or->flush[0]));
or->flush[or->nflush++] = r;
qunlock(&or->lk);
return -1; /* delay response until or is responded */
}
qunlock(&or->lk);
closereq(or);
}
r->oldreq = nil;
return 0;
}
static char*
oldwalk1(Fid *fid, char *name, void *arg)
{
char *e;
Qid qid;
Srv *srv;
srv = arg;
e = srv->walk1(fid, name, &qid);
if(e)
return e;
fid->qid = qid;
return nil;
}
static char*
oldclone(Fid *fid, Fid *newfid, void *arg)
{
Srv *srv;
srv = arg;
if(srv->clone == nil)
return nil;
return srv->clone(fid, newfid);
}
static void
swalk(Srv *srv, Req *r)
{
if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
respond(r, Eunknownfid);
return;
}
if(r->fid->omode != -1){
respond(r, "cannot clone open fid");
return;
}
if(r->ifcall.nwname && !(r->fid->qid.type&QTDIR)){
respond(r, Ewalknodir);
return;
}
if(r->ifcall.fid != r->ifcall.newfid){
if((r->newfid = allocfid(srv->fpool, r->ifcall.newfid)) == nil){
respond(r, Edupfid);
return;
}
r->newfid->uid = estrdup9p(r->fid->uid);
}else{
incref(&r->fid->ref);
r->newfid = r->fid;
}
if(r->fid->file){
filewalk(r);
}else if(srv->walk1)
walkandclone(r, oldwalk1, oldclone, srv);
else if(srv->walk)
srv->walk(r);
else
sysfatal("no walk function, no file trees");
}
static void
rwalk(Req *r, char *error)
{
if(error || r->ofcall.nwqid < r->ifcall.nwname){
if(r->ifcall.fid != r->ifcall.newfid && r->newfid)
closefid(removefid(r->srv->fpool, r->newfid->fid));
if (r->ofcall.nwqid==0){
if(error==nil && r->ifcall.nwname!=0)
r->error = Enotfound;
}else
r->error = nil; // No error on partial walks
}else{
if(r->ofcall.nwqid == 0){
/* Just a clone */
r->newfid->qid = r->fid->qid;
}else{
/* if file trees are in use, filewalk took care of the rest */
r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1];
}
}
}
static void
sopen(Srv *srv, Req *r)
{
int p;
if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
respond(r, Eunknownfid);
return;
}
if(r->fid->omode != -1){
respond(r, Ebotch);
return;
}
if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){
respond(r, Eisdir);
return;
}
r->ofcall.qid = r->fid->qid;
switch(r->ifcall.mode&3){
default:
assert(0);
case OREAD:
p = AREAD;
break;
case OWRITE:
p = AWRITE;
break;
case ORDWR:
p = AREAD|AWRITE;
break;
case OEXEC:
p = AEXEC;
break;
}
if(r->ifcall.mode&OTRUNC)
p |= AWRITE;
if((r->fid->qid.type&QTDIR) && p!=AREAD){
respond(r, Eperm);
return;
}
if(r->fid->file){
if(!hasperm(r->fid->file, r->fid->uid, p)){
respond(r, Eperm);
return;
}
/* BUG RACE */
if((r->ifcall.mode&ORCLOSE)
&& !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
respond(r, Eperm);
return;
}
r->ofcall.qid = r->fid->file->qid;
if((r->ofcall.qid.type&QTDIR)
&& (r->fid->rdir = opendirfile(r->fid->file)) == nil){
respond(r, "opendirfile failed");
return;
}
}
if(srv->open)
srv->open(r);
else
respond(r, nil);
}
static void
ropen(Req *r, char *error)
{
char errbuf[ERRMAX];
if(error)
return;
if(chatty9p){
snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode);
write(2, errbuf, strlen(errbuf));
}
r->fid->omode = r->ifcall.mode;
r->fid->qid = r->ofcall.qid;
if(r->ofcall.qid.type&QTDIR)
r->fid->diroffset = 0;
}
static void
screate(Srv *srv, Req *r)
{
if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil)
respond(r, Eunknownfid);
else if(r->fid->omode != -1)
respond(r, Ebotch);
else if(!(r->fid->qid.type&QTDIR))
respond(r, Ecreatenondir);
else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE))
respond(r, Eperm);
else if(srv->create)
srv->create(r);
else
respond(r, Enocreate);
}
static void
rcreate(Req *r, char *error)
{
if(error)
return;
r->fid->omode = r->ifcall.mode;
r->fid->qid = r->ofcall.qid;
}
static void
sread(Srv *srv, Req *r)
{
int o;
if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
respond(r, Eunknownfid);
return;
}
if((int)r->ifcall.count < 0){
respond(r, Ebotch);
return;
}
if(r->ifcall.offset < 0
|| ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){
respond(r, Ebadoffset);
return;
}
if(r->ifcall.count > srv->msize - IOHDRSZ)
r->ifcall.count = srv->msize - IOHDRSZ;
r->rbuf = emalloc9p(r->ifcall.count);
r->ofcall.data = r->rbuf;
o = r->fid->omode & 3;
if(o != OREAD && o != ORDWR && o != OEXEC){
respond(r, Ebotch);
return;
}
if((r->fid->qid.type&QTDIR) && r->fid->file){
r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count);
respond(r, nil);
return;
}
if(srv->read)
srv->read(r);
else
respond(r, "no srv->read");
}
static void
rread(Req *r, char *error)
{
if(error==nil && (r->fid->qid.type&QTDIR))
r->fid->diroffset += r->ofcall.count;
}
static void
swrite(Srv *srv, Req *r)
{
int o;
char e[ERRMAX];
if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
respond(r, Eunknownfid);
return;
}
if((int)r->ifcall.count < 0){
respond(r, Ebotch);
return;
}
if(r->ifcall.offset < 0){
respond(r, Ebotch);
return;
}
if(r->ifcall.count > srv->msize - IOHDRSZ)
r->ifcall.count = srv->msize - IOHDRSZ;
o = r->fid->omode & 3;
if(o != OWRITE && o != ORDWR){
snprint(e, sizeof e, "write on fid with open mode 0x%ux", r->fid->omode);
respond(r, e);
return;
}
if(srv->write)
srv->write(r);
else
respond(r, "no srv->write");
}
static void
rwrite(Req *r, char *error)
{
if(error)
return;
if(r->fid->file)
r->fid->file->qid.vers++;
}
static void
sclunk(Srv *srv, Req *r)
{
if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil)
respond(r, Eunknownfid);
else
respond(r, nil);
}
static void
rclunk(Req*, char*)
{
}
static void
sremove(Srv *srv, Req *r)
{
if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){
respond(r, Eunknownfid);
return;
}
/* BUG RACE */
if(r->fid->file && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
respond(r, Eperm);
return;
}
if(srv->remove)
srv->remove(r);
else
respond(r, r->fid->file ? nil : Enoremove);
}
static void
rremove(Req *r, char *error, char *errbuf)
{
if(error)
return;
if(r->fid->file){
if(removefile(r->fid->file) < 0){
snprint(errbuf, ERRMAX, "remove %s: %r",
r->fid->file->name);
r->error = errbuf;
}
r->fid->file = nil;
}
}
static void
sstat(Srv *srv, Req *r)
{
if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
respond(r, Eunknownfid);
return;
}
if(r->fid->file){
/* should we rlock the file? */
r->d = r->fid->file->Dir;
if(r->d.name)
r->d.name = estrdup9p(r->d.name);
if(r->d.uid)
r->d.uid = estrdup9p(r->d.uid);
if(r->d.gid)
r->d.gid = estrdup9p(r->d.gid);
if(r->d.muid)
r->d.muid = estrdup9p(r->d.muid);
}
if(srv->stat)
srv->stat(r);
else if(r->fid->file)
respond(r, nil);
else
respond(r, Enostat);
}
static void
rstat(Req *r, char *error)
{
int n;
uchar *statbuf;
uchar tmp[BIT16SZ];
if(error)
return;
if(convD2M(&r->d, tmp, BIT16SZ) != BIT16SZ){
r->error = "convD2M(_,_,BIT16SZ) did not return BIT16SZ";
return;
}
n = GBIT16(tmp)+BIT16SZ;
statbuf = emalloc9p(n);
if(statbuf == nil){
r->error = "out of memory";
return;
}
r->ofcall.nstat = convD2M(&r->d, statbuf, n);
r->ofcall.stat = statbuf; /* freed in closereq */
if(r->ofcall.nstat <= BIT16SZ){
r->error = "convD2M fails";
free(statbuf);
return;
}
}
static void
swstat(Srv *srv, Req *r)
{
if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
respond(r, Eunknownfid);
return;
}
if(srv->wstat == nil){
respond(r, Enowstat);
return;
}
if(convM2D(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat) != r->ifcall.nstat){
respond(r, Ebaddir);
return;
}
if((ushort)~r->d.type){
respond(r, "wstat -- attempt to change type");
return;
}
if((uint)~r->d.dev){
respond(r, "wstat -- attempt to change dev");
return;
}
if((uchar)~r->d.qid.type || (ulong)~r->d.qid.vers || (uvlong)~r->d.qid.path){
respond(r, "wstat -- attempt to change qid");
return;
}
if(r->d.muid && r->d.muid[0]){
respond(r, "wstat -- attempt to change muid");
return;
}
if((ulong)~r->d.mode && ((r->d.mode&DMDIR)>>24) != (r->fid->qid.type&QTDIR)){
respond(r, "wstat -- attempt to change DMDIR bit");
return;
}
srv->wstat(r);
}
static void
rwstat(Req*, char*)
{
}
void
srv(Srv *srv)
{
Req *r;
fmtinstall('D', dirfmt);
fmtinstall('F', fcallfmt);
if(srv->fpool == nil)
srv->fpool = allocfidpool(srv->destroyfid);
if(srv->rpool == nil)
srv->rpool = allocreqpool(srv->destroyreq);
if(srv->msize == 0)
srv->msize = 8192+IOHDRSZ;
changemsize(srv, srv->msize);
srv->fpool->srv = srv;
srv->rpool->srv = srv;
while(r = getreq(srv)){
if(r->error){
respond(r, r->error);
continue;
}
switch(r->ifcall.type){
default:
respond(r, "unknown message");
break;
case Tversion: sversion(srv, r); break;
case Tauth: sauth(srv, r); break;
case Tattach: sattach(srv, r); break;
case Tflush: sflush(srv, r); break;
case Twalk: swalk(srv, r); break;
case Topen: sopen(srv, r); break;
case Tcreate: screate(srv, r); break;
case Tread: sread(srv, r); break;
case Twrite: swrite(srv, r); break;
case Tclunk: sclunk(srv, r); break;
case Tremove: sremove(srv, r); break;
case Tstat: sstat(srv, r); break;
case Twstat: swstat(srv, r); break;
}
}
free(srv->rbuf);
srv->rbuf = nil;
free(srv->wbuf);
srv->wbuf = nil;
srv->msize = 0;
freefidpool(srv->fpool);
srv->fpool = nil;
freereqpool(srv->rpool);
srv->rpool = nil;
if(srv->end)
srv->end(srv);
}
void
respond(Req *r, char *error)
{
int i, m, n;
char errbuf[ERRMAX];
Srv *srv;
srv = r->srv;
assert(srv != nil);
assert(r->responded == 0);
r->error = error;
switch(r->ifcall.type){
default:
assert(0);
/*
* Flush is special. If the handler says so, we return
* without further processing. Respond will be called
* again once it is safe.
*/
case Tflush:
if(rflush(r, error)<0)
return;
break;
case Tversion: rversion(r, error); break;
case Tauth: rauth(r, error); break;
case Tattach: rattach(r, error); break;
case Twalk: rwalk(r, error); break;
case Topen: ropen(r, error); break;
case Tcreate: rcreate(r, error); break;
case Tread: rread(r, error); break;
case Twrite: rwrite(r, error); break;
case Tclunk: rclunk(r, error); break;
case Tremove: rremove(r, error, errbuf); break;
case Tstat: rstat(r, error); break;
case Twstat: rwstat(r, error); break;
}
r->ofcall.tag = r->ifcall.tag;
r->ofcall.type = r->ifcall.type+1;
if(r->error)
setfcallerror(&r->ofcall, r->error);
if(chatty9p)
fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall);
qlock(&srv->wlock);
n = convS2M(&r->ofcall, srv->wbuf, srv->msize);
if(n <= 0){
fprint(2, "n = %d %F\n", n, &r->ofcall);
abort();
}
assert(n > 2);
if(r->pool) /* not a fake */
closereq(removereq(r->pool, r->ifcall.tag));
m = write(srv->outfd, srv->wbuf, n);
if(m != n)
sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd);
qunlock(&srv->wlock);
qlock(&r->lk); /* no one will add flushes now */
r->responded = 1;
qunlock(&r->lk);
for(i=0; i<r->nflush; i++)
respond(r->flush[i], nil);
free(r->flush);
r->flush = nil;
r->nflush = 0;
if(r->pool)
closereq(r);
else
free(r);
}
void
responderror(Req *r)
{
char errbuf[ERRMAX];
rerrstr(errbuf, sizeof errbuf);
respond(r, errbuf);
}
int
postfd(char *name, int pfd)
{
int fd;
char buf[80];
snprint(buf, sizeof buf, "/srv/%s", name);
if(chatty9p)
fprint(2, "postfd %s\n", buf);
fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600);
if(fd < 0){
if(chatty9p)
fprint(2, "create fails: %r\n");
return -1;
}
if(fprint(fd, "%d", pfd) < 0){
if(chatty9p)
fprint(2, "write fails: %r\n");
close(fd);
return -1;
}
if(chatty9p)
fprint(2, "postfd successful\n");
return 0;
}

25
sys/src/lib9p/thread.c Executable file
View file

@ -0,0 +1,25 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
static void
tforker(void (*fn)(void*), void *arg, int rflag)
{
procrfork(fn, arg, 32*1024, rflag);
}
void
threadlistensrv(Srv *s, char *addr)
{
_forker = tforker;
_listensrv(s, addr);
}
void
threadpostmountsrv(Srv *s, char *name, char *mtpt, int flag)
{
_forker = tforker;
_postmountsrv(s, name, mtpt, flag);
}

34
sys/src/lib9p/uid.c Executable file
View file

@ -0,0 +1,34 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
/*
* simplistic permission checking. assume that
* each user is the leader of her own group.
*/
int
hasperm(File *f, char *uid, int p)
{
int m;
m = f->mode & 7; /* other */
if((p & m) == p)
return 1;
if(strcmp(f->uid, uid) == 0) {
m |= (f->mode>>6) & 7;
if((p & m) == p)
return 1;
}
if(strcmp(f->gid, uid) == 0) {
m |= (f->mode>>3) & 7;
if((p & m) == p)
return 1;
}
return 0;
}

25
sys/src/lib9p/util.c Executable file
View file

@ -0,0 +1,25 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include "9p.h"
void
readbuf(Req *r, void *s, long n)
{
r->ofcall.count = r->ifcall.count;
if(r->ifcall.offset >= n){
r->ofcall.count = 0;
return;
}
if(r->ifcall.offset+r->ofcall.count > n)
r->ofcall.count = n - r->ifcall.offset;
memmove(r->ofcall.data, (char*)s+r->ifcall.offset, r->ofcall.count);
}
void
readstr(Req *r, char *s)
{
readbuf(r, s, strlen(s));
}