#include <u.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
ulong messagesize = IOHDRSZ+8192;
char statbuf[IOHDRSZ+8192];
int subfd;
int editfd = -1;
int tidefd = -1;
int old9p = -1;
uchar* rxbuf;
uchar* txbuf;
char Ewstatbuffer[] = "bogus wstat buffer";
void
usage(void)
{
fprint(2, "usage: substfs [...] /srv/service | -c command | -n networkaddress [remote2local local2remote]\n");
exits("usage");
}
void*
emalloc(ulong n)
{
void *p;
p = malloc(n);
if(p == 0)
sysfatal("malloc(%ld) fails", (long)n);
memset(p, 0, n);
return p;
}
void
getfcallnew(int fd, Fcall *fc, int have)
{
int len;
if(have > BIT32SZ)
sysfatal("cannot happen: %r");
if(have < BIT32SZ && readn(fd, rxbuf+have, BIT32SZ-have) != BIT32SZ-have)
sysfatal("couldn't read message: %r");
len = GBIT32(rxbuf);
if(len <= BIT32SZ)
sysfatal("bogus message");
len -= BIT32SZ;
if(readn(fd, rxbuf+BIT32SZ, len) != len)
sysfatal("short message: %r");
if(convM2S(rxbuf, len+BIT32SZ, fc) != len+BIT32SZ)
sysfatal("badly sized message type %d: %r", rxbuf[0]);
}
void
putfcallnew(int wfd, Fcall *tx)
{
ulong n;
if((n = convS2M(tx, txbuf, messagesize)) == 0)
sysfatal("couldn't format message type %d: %r", tx->type);
fprint(2, "substfs: %F\n", tx);
if(write(wfd, txbuf, n) != n)
sysfatal("couldn't send message: %r");
}
void
getfcall(int fd, Fcall *fc)
{
//getfcallnew(fd, fc, 0);
int res;
res = read9pmsg(fd, rxbuf, messagesize);
if (res < 0) {
// fprint(2, "%us", rxbuf);
sysfatal("couldn't read message: %r");
}
if (res == 0)
sysfatal("end of file on fd");
if(convM2S(rxbuf, messagesize, fc) == 0)
sysfatal("badly sized message type %d: %r", rxbuf[0]);
fprint(2, "substfs: %F\n", fc);
}
static
long
dirpackage(uchar *buf, long ts, Dir **d)
{
char *s;
long ss, i, n, nn, m;
*d = nil;
if(ts <= 0)
return 0;
/*
* first find number of all stats, check they look like stats, & size all associated strings
*/
ss = 0;
n = 0;
for(i = 0; i < ts; i += m){
m = BIT16SZ + GBIT16(&buf[i]);
if(statcheck(&buf[i], m) < 0)
break;
ss += m;
n++;
}
if(i != ts)
return -1;
*d = malloc(n * sizeof(Dir) + ss);
if(*d == nil)
return -1;
/*
* then convert all buffers
*/
s = (char*)*d + n * sizeof(Dir);
nn = 0;
for(i = 0; i < ts; i += m){
m = BIT16SZ + GBIT16((uchar*)&buf[i]);
if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){
free(*d);
*d = nil;
return -1;
}
nn++;
s += m;
}
return nn;
}
void
mydirread9p(Fcall *rx, Fcall *tx, Dir* d, long ndir)
{
int start;
uchar *p, *ep;
uint rv;
if(rx->offset == 0)
start = 0;
else
// start = r->fid->dirindex;
start = 0;
p = (uchar*)tx->data;
ep = p+rx->count;
while((p < ep) && (start < ndir)){
rv = convD2M(&d[start], p, ep-p);
if(rv <= BIT16SZ)
break;
p += rv;
start++;
}
// r->fid->dirindex = start;
tx->count = p - (uchar*)tx->data;
}
static
char*
do_edit(int fd, char *src, char *fn)
{
char buf[1024];
if (fd >= 0) {
fprint(2, "substfs: %swrite... %s\n", fn, src);
write(fd, src, strlen(src)+1);
fprint(2, "substfs: %swrite... done\n", fn);
fprint(2, "substfs: %sread... \n", fn);
read(fd, buf, sizeof(buf));
fprint(2, "substfs: %sread... %s\n", fn, buf);
if (strcmp(src, buf) != 0) {
return strdup(buf);
}
}
return src;
}
static
char*
do_tide(int fd, char *src, char *fn)
{
char buf[1024];
char *p;
if (fd >= 0) {
fprint(2, "substfs: %swrite... %s\n", fn, src);
fprint(fd, "%s\n", src);
fprint(2, "substfs: %swrite... done\n", fn);
fprint(2, "substfs: %sread... \n", fn);
read(fd, buf, sizeof(buf));
if (p = strchr(buf, '\n'))
*p = '\0';
fprint(2, "substfs: %sread... %s\n", fn, buf);
if (strcmp(src, buf) != 0) {
return strdup(buf);
}
}
return src;
}
static
char*
edit(char *src)
{
return do_edit(editfd, src, "edit");
}
static
char*
tide(char *src)
{
return do_tide(tidefd, src, "tide");
}
static
void
handle(Fcall *rx, Fcall *tx)
{
fprint(2, "substfs: putfcallnew...\n");
putfcallnew(subfd, rx);
fprint(2, "substfs: putfcallnew... done\n");
fprint(2, "substfs: getfcall... \n");
getfcall(subfd, tx);
fprint(2, "substfs: getfcall... done\n");
}
void
seterror(Fcall *f, char *error)
{
f->type = Rerror;
f->ename = error ? error : "programmer error";
}
void
rversion(Fcall *rx, Fcall *tx, Fidpool *fp)
{
USED(fp);
handle(rx, tx);
}
void
rauth(Fcall *rx, Fcall *tx, Fidpool *fp)
{
Fid *f = allocfid(fp, rx->afid);
handle(rx, tx);
fprint(2, "substfs: fid: %ld\n", f->fid);
f->qid = tx->qid;
closefid(f);
}
void
rattach(Fcall *rx, Fcall *tx, Fidpool *fp)
{
Fid *f = allocfid(fp, rx->fid);
handle(rx, tx);
fprint(2, "substfs: fid: %ld\n", f->fid);
f->qid = tx->qid;
closefid(f);
}
void
rwalk(Fcall *rx, Fcall *tx, Fidpool *fp)
{
Fid *f;
long i;
for (i = 0; i < rx->nwname; i++) {
rx->wname[i] = tide(rx->wname[i]);
}
handle(rx, tx);
if ((rx->nwname == tx->nwqid)) {
f= allocfid(fp, rx->newfid);
fprint(2, "substfs: fid: %ld\n", f->fid);
f->qid = tx->wqid[tx->nwqid-1];
closefid(f);
} else {
fprint(2, "substfs: walk failed\n");
}
}
void
ropen(Fcall *rx, Fcall *tx, Fidpool *fp)
{
Fid *f= lookupfid(fp, rx->fid);
handle(rx, tx);
fprint(2, "substfs: fid: %ld\n", f->fid);
f->qid = tx->qid;
closefid(f);
}
void
rcreate(Fcall *rx, Fcall *tx, Fidpool *fp)
{
Fid *f;
handle(rx, tx);
if (tx->type != Rerror) {
f= allocfid(fp, rx->fid);
fprint(2, "substfs: fid: %ld\n", f->fid);
f->qid = tx->qid;
closefid(f);
} else {
fprint(2, "substfs: create failed\n");
}
}
void
rread(Fcall *rx, Fcall *tx, Fidpool *fp)
{
Dir *d;
long nd;
long i;
Fid *f= lookupfid(fp, rx->fid);
handle(rx, tx);
fprint(2, "substfs: fid: %ld\n", f->fid);
fprint(2, "substfs: qid.type: %x\n", f->qid.type);
if (f->qid.type&QTDIR) {
fprint(2, "substfs: directory!\n");
nd = dirpackage((uchar*)tx->data, tx->count, &d);
for (i=0; i < nd; i++) {
fprint(2, "substfs-rread[%ld]: %D\n", i, &d[i]);
d[i].name = edit(d[i].name);
}
mydirread9p(rx, tx, d, nd);
} else {
fprint(2, "substfs: NO directory!\n");
}
closefid(f);
}
void
rwrite(Fcall *rx, Fcall *tx, Fidpool *fp)
{
Fid *f= lookupfid(fp, rx->fid);
handle(rx, tx);
fprint(2, "substfs: fid: %ld\n", f->fid);
closefid(f);
}
void
rclunk(Fcall *rx, Fcall *tx, Fidpool *fp)
{
Fid *f= lookupfid(fp, rx->fid);
handle(rx, tx);
fprint(2, "substfs: fid: %ld\n", f->fid);
removefid(fp, rx->fid);
}
void
rremove(Fcall *rx, Fcall *tx, Fidpool *fp)
{
Fid *f= lookupfid(fp, rx->fid);
handle(rx, tx);
fprint(2, "substfs: fid: %ld\n", f->fid);
closefid(f);
}
void
rstat(Fcall *rx, Fcall *tx, Fidpool *fp)
{
Fid *f= lookupfid(fp, rx->fid);
Dir d;
handle(rx, tx);
fprint(2, "substfs: fid: %ld\n", f->fid);
if (convM2D(tx->stat, tx->nstat, &d, statbuf) <= BIT16SZ){
seterror(tx, Ewstatbuffer);
fprint(2, "substfs-rstat: Ewstatbuffer\n");
} else {
fprint(2, "substfs-rstat: %D\n", &d);
}
closefid(f);
}
void
rwstat(Fcall *rx, Fcall *tx, Fidpool *fp)
{
Fid *f= lookupfid(fp, rx->fid);
handle(rx, tx);
fprint(2, "substfs: fid: %ld\n", f->fid);
closefid(f);
}
void
fidnop(Fid*)
{
}
void
serve(int rfd, int wfd)
{
Fcall rx, tx;
Fidpool* fp;
fp = allocfidpool(fidnop);
for(;;){
getfcall(rfd, &rx);
if(chatty9p)
fprint(2, "<- %F\n", &rx);
memset(&tx, 0, sizeof tx);
tx.type = rx.type+1;
tx.tag = rx.tag;
switch(rx.type){
case Tflush:
break;
case Tversion:
rversion(&rx, &tx, fp);
break;
case Tauth:
rauth(&rx, &tx, fp);
break;
case Tattach:
rattach(&rx, &tx, fp);
break;
case Twalk:
rwalk(&rx, &tx, fp);
break;
case Tstat:
// tx.stat = databuf;
rstat(&rx, &tx, fp);
break;
case Twstat:
rwstat(&rx, &tx, fp);
break;
case Topen:
ropen(&rx, &tx, fp);
break;
case Tcreate:
rcreate(&rx, &tx, fp);
break;
case Tread:
// tx.data = databuf;
rread(&rx, &tx, fp);
break;
case Twrite:
rwrite(&rx, &tx, fp);
break;
case Tclunk:
rclunk(&rx, &tx, fp);
break;
case Tremove:
rremove(&rx, &tx, fp);
break;
default:
fprint(2, "unknown message %F\n", &rx);
seterror(&tx, "bad message");
break;
}
if(chatty9p)
fprint(2, "-> %F\n", &tx);
putfcallnew(wfd, &tx);
}
}
int
connectcmd(char *cmd)
{
int p[2];
if(pipe(p) < 0)
return -1;
switch(fork()){
case -1:
fprint(2, "fork failed: %r\n");
_exits("exec");
case 0:
dup(p[0], 0);
dup(p[0], 1);
close(p[1]);
execl("/bin/rc", "rc", "-c", cmd, nil);
fprint(2, "exec failed: %r\n");
_exits("exec");
default:
close(p[0]);
return p[1];
}
}
void
main(int argc, char **argv)
{
char *mtpt, *service;
int cmd, net;
int sfd[2];
mtpt = nil;
service = nil;
cmd = 0;
net = 0;
ARGBEGIN{
case 'D':
chatty9p++;
break;
case 's':
service = EARGF(usage());
break;
case 'm':
mtpt = EARGF(usage());
break;
case 'c':
cmd = 1;
break;
case 'n':
net = 1;
break;
default:
usage();
}ARGEND
if(argc < 1)
usage();
fmtinstall('D', dirfmt);
fmtinstall('M', dirmodefmt);
fmtinstall('F', fcallfmt);
rxbuf = emalloc(messagesize);
txbuf = emalloc(messagesize);
if(cmd && net)
usage();
if(cmd)
subfd = connectcmd(argv[0]);
else if(net){
subfd = dial(netmkaddr(argv[0], "net", "9fs"), 0, 0, 0);
if(subfd < 0)
sysfatal("dial: %r");
}else{
subfd = open(argv[0], ORDWR);
if(subfd < 0)
sysfatal("open: %r");
}
//postmountsrv(&substsrv, service, mtpt, MREPL);
//exits(nil);
if (argc == 3) {
editfd = connectcmd(argv[1]);
tidefd = connectcmd(argv[2]);
}
if(pipe(sfd) < 0)
sysfatal("pipe: %r");
if(service)
if(postfd(service, sfd[0]) < 0)
sysfatal("postfd %s: %r", service);
serve(sfd[1], sfd[1]);
}
|