shithub: iso

ref: e4322f4dffdea1ac61d67e2f0852982c65f5e98d
dir: /src/execfs/execfs.c/

View raw version
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>

#include <bio.h>
#include <regexp.h>

typedef struct Xrule Xrule;
typedef struct Xfile Xfile;

struct Xrule
{
	Reprog	*re;
	char	*sub;
	Xrule	*next;
};

struct Xfile
{
	char	*path;
	char	*dest;
	int	fd;
};

Xrule *rules;

void
readrules(char *file)
{
	Biobuf *bio;
	char *s, *p, *d;

	if((bio = Bopen(file, OREAD)) == nil)
		sysfatal("open: %r");
	while(s = Brdstr(bio, '\n', 1)){
		Xrule *r;

		p = s;
		while(strchr("\t ", *p))
			p++;
		d = nil;
		if(*p != '#'){
			if(d = strchr(p, '\t'))
				*d++ = 0;
			else if(d = strchr(p, ' '))
				*d++ = 0;
		}
		if(d == nil){
			free(s);
			continue;
		}
		while(strchr("\t ", *d))
			d++;

		r = emalloc9p(sizeof(*r));
		if(r->re = regcomp(p)){
			r->sub = estrdup9p(d);
			r->next = rules;
			rules = r;
		} else {
			fprint(2, "regcomp failed: %s\n", p);
			free(r);
		}
		free(s);
	}
	Bterm(bio);
}

char*
matchrule(char *path)
{
	Xrule *r;
	Resub m[16];
	char *s;

	for(r = rules; r; r = r->next){
		memset(m, 0, sizeof(m));
		if(regexec(r->re, path, m, nelem(m))){
			s = emalloc9p(1024);
			regsub(r->sub, s, 1024, m, nelem(m));
			return s;
		}
	}
	return nil;
}

ulong
hash(char *s)
{
	ulong h, t;
	char c;

	h = 0;
	while(c = *s++){
		t = h & 0xf8000000;
		h <<= 5;
		h ^= t>>27;
		h ^= (ulong)c;
	}
	return h;
}

void
fsattach(Req *req)
{
	Xfile *x;

	if(req->ifcall.aname && req->ifcall.aname[0]){
		respond(req, "invalid attach specifier");
		return;
	}

	x = emalloc9p(sizeof(*x));
	x->path = estrdup9p("");
	x->dest = nil;
	x->fd = -1;

	req->fid->aux = x;
	req->fid->qid.path = hash(x->path);
	req->fid->qid.type = QTDIR;
	req->fid->qid.vers = 0;
	req->ofcall.qid = req->fid->qid;

	respond(req, nil);
}

char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
	Xfile *x;
	char *p;

	if(fid->qid.type != QTDIR)
		return "walk in non-directory";

	x = fid->aux;
	if(strcmp(name, ".") == 0){
	} else if(strcmp(name, "..") == 0){
		if(p = strrchr(x->path, '/'))
			*p = 0;
	} else {
		p = smprint("%s/%s", x->path, name);
		free(x->path);
		x->path = p;
	}
	fid->qid.path = hash(x->path);
	if(x->dest = matchrule(x->path))
		fid->qid.type = QTFILE;
	else
		fid->qid.type = QTDIR;
	if(qid)
		*qid = fid->qid;
	return nil;
}

char*
fsclone(Fid *old, Fid *new)
{
	Xfile *x;

	x = emalloc9p(sizeof(*x));
	memmove(x, old->aux, sizeof(*x));
	if(x->path)
		x->path = estrdup9p(x->path);
	if(x->dest)
		x->dest = estrdup9p(x->dest);
	new->aux = x;
	return nil;
}

void
fsstat(Req *req)
{
	Xfile *x;
	char *p;
	Dir *d;

	x = req->fid->aux;
	d = &req->d;
	memset(d, 0, sizeof(*d));
	d->uid = estrdup9p("execfs");
	d->gid = estrdup9p("execfs");
	d->atime = d->mtime = time(0);
	if(p = strrchr(x->path, '/'))
		d->name = estrdup9p(p+1);
	else
		d->name = estrdup9p("/");
	if(x->dest){
		d->qid.type = QTFILE;
		d->mode = 0444;
	}else {
		d->qid.type = QTDIR;
		d->mode = DMDIR|0555;
	}
	respond(req, nil);
}

void
fsread(Req *req)
{
	int n, pfd[2];
	Xfile *x;
	Srv *srv;

	x = req->fid->aux;
	if(x->dest == nil){
		req->ofcall.count = 0;
		respond(req, nil);
		return;
	}
	if(x->fd < 0){
		if(pipe(pfd) < 0){
			responderror(req);
			return;
		}
		if(rfork(RFPROC|RFNOWAIT|RFFDG|RFREND) == 0){
			char *argv[4];
			int i;

			argv[0] = "rc";
			argv[1] = "-c";
			argv[2] = x->dest;
			argv[3] = nil;

			close(0);
			open("/dev/null", OREAD);
			dup(pfd[1], 1);
			dup(pfd[1], 2);
			for(i=3; i<20; i++)
				close(i);
			exec("/bin/rc", argv);
			exits("exec");
		}
		x->fd = pfd[0];
		close(pfd[1]);
	}
	srvrelease(srv = req->srv);
	if((n = read(x->fd, req->ofcall.data, req->ifcall.count)) < 0)
		responderror(req);
	else { 
		req->ofcall.count = n;
		respond(req, nil);
	}
	srvacquire(srv);
}

void
fsclunk(Fid *fid)
{
	Xfile *x;

	if(x = fid->aux){
		fid->aux = nil;

		if(x->fd >= 0)
			close(x->fd);
		free(x->path);
		free(x->dest);
		free(x);
	}
}

Srv fs = {
	.attach = fsattach,
	.stat = fsstat,
	.read = fsread,
	.walk1 = fswalk1,
	.clone = fsclone,
	.destroyfid = fsclunk,
};

void
usage(void)
{
	fprint(2, "usage: %s [-D] [-m mtpt] [-s srv] rulefile\n", argv0);
	exits("usage");
}

void
main(int argc, char *argv[])
{
	char *srv, *mtpt;

	srv = nil;
	mtpt = "/n/execfs";
	ARGBEGIN {
	case 'D':
		chatty9p++;
		break;
	case 'm':
		mtpt = EARGF(usage());
		break;
	case 's':
		srv = EARGF(usage());
		break;
	default:
		usage();
	} ARGEND;

	if(argc != 1)
		usage();

	readrules(*argv);
	postmountsrv(&fs, srv, mtpt, MREPL);
}