Import sources from 2011-03-30 iso image
This commit is contained in:
commit
e5888a1ffd
7810 changed files with 2489717 additions and 0 deletions
198
sys/src/lib9p/auth.c
Executable file
198
sys/src/lib9p/auth.c
Executable 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
40
sys/src/lib9p/dirread.c
Executable 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
82
sys/src/lib9p/fid.c
Executable 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
415
sys/src/lib9p/file.c
Executable 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
29
sys/src/lib9p/ftest.c
Executable 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
165
sys/src/lib9p/intmap.c
Executable 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
103
sys/src/lib9p/listen.c
Executable 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
49
sys/src/lib9p/mem.c
Executable 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
32
sys/src/lib9p/mkfile
Executable 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
116
sys/src/lib9p/parse.c
Executable 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
71
sys/src/lib9p/post.c
Executable 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
170
sys/src/lib9p/ramfs.c
Executable 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
113
sys/src/lib9p/req.c
Executable 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
34
sys/src/lib9p/rfork.c
Executable 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
855
sys/src/lib9p/srv.c
Executable 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
25
sys/src/lib9p/thread.c
Executable 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
34
sys/src/lib9p/uid.c
Executable 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
25
sys/src/lib9p/util.c
Executable 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));
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue