Plan 9 from Bell Labs’s /usr/web/sources/contrib/maht/actionfs.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


// Put 

//	8c -w actionfs.c &&  8l actionfs.8  && mv 8.out /usr/maht/bin/386/actionfs 


#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <regexp.h>
#include <stdio.h>

typedef struct Path Path;
struct Path
{
	Qid qid;
	char *name;
	Path *next;
};

Reprog  *freg;
Path *root = nil;
int nmatches;
int client = 0;

static void
fsattach(Req *r)
{
	r->ofcall.qid = (Qid){0, ++client, QTDIR};
	r->fid->qid = r->ofcall.qid;
	respond(r, nil);
}

static void
print_qid(Qid *q) {
	print("p %x v %d f %x\n", q->path, q->vers, q->type);
}

static void
print_matches(Resub *matches) {
	if(!matches) {
		print("No match\n");
		return;
	}

	char *bit;
	int i, k;
	for(i = 0; i < nmatches; i++) {
		k = (matches[i].ep - matches[i].sp) + 1;
		bit = (char*)malloc(k);
		strecpy(bit, bit + k, matches[i].sp);
		free(bit);
	}
	print("\n");
}

static void
print_path(Path *p) {
	print("Name: %s\n", p->name);
	print_qid(&p->qid);
	print("Next: %x\n", p->next);
}

static Resub*
re(char *txt) {
	Resub* matches = (Resub*)calloc(nmatches, sizeof(Resub));
	if(regexec(freg, txt, matches, nmatches))
		return matches;
	free(matches);
	return nil;	
}

static Path*
find_path(Qid *qid) {
	Path *p;
	for(p = root; p; p = p->next) 
		if(qid->path == p->qid.path)
			break;
	return p;
}

static Path*
find_prev_path(Qid *qid) {
	Path *p;
	for(p = root; p; p = p->next) {
		if(p->next && (qid->path == p->next->qid.path))
			break;
	}
	return p;
}

static Qid*
find_qid(char *name) {
	Path *p;
	Resub *m;
	for(p = root; p; p = p->next)
		if(strcmp(name, p->name) == 0)
			return &p->qid;
	if(!(m = re(name)))
		return nil;
	free(m);

	p = (Path*)mallocz(sizeof(Path), 1);
	p->qid.path = root ? root->qid.path +1 : 1;
	p->qid.vers = 0;
	p->next = root;
	p->name = strdup(name);
	root = p;
	return &root->qid;
}

static char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
	Qid *q;
	
	if(!(q = find_qid(name))) 
		return "Not Found";

	q->vers++;
	memcpy(qid, q, sizeof(Qid));
	memcpy(&fid->qid, q, sizeof(Qid));

	return nil;
}

static void
fsstat(Req *r)
{
	Path *p;
	Dir *d = &r->d;
	memset(d, 0, sizeof *d);
	d->uid = strdup("inband");
	d->gid = strdup("inband");

	p = find_path(&r->fid->qid);
	d->name = strdup(p->name);
	d->mode = 0444;
	memcpy(&d->qid, &(r->fid->qid), sizeof(Qid));
	d->length = 0;
	respond(r, nil);
}

char **
build_argv(int fd, char *name) {
	char **argv = malloc(sizeof(char*) * (nmatches + 3));
	if(fd > 0)
		argv[0] = smprint("action-read");
	else
		argv[0] = smprint("action-write");

	argv[1] = smprint("%d", abs(fd));

	Resub *matches = re(name);
	int i, j, k;
	for(i = 0, j = 2; i < nmatches; i++, j++) {
		k = (matches[i].ep - matches[i].sp) + 1;
		argv[j] = (char*)mallocz(k + 1, 1);
		strecpy(argv[j], argv[j] + k, matches[i].sp);
	}
	argv[j] = nil;

	return argv;
}

static char *
do_action(char *action, int fd, Path *p) {
	char **argv = build_argv(fd, p->name);
	char *error = nil;
	int i;

	switch(fork()) {
	case 0 :
		exec(action, argv);
		error = "exec failed";
		break;
	case -1 :
		error = "fork failed";
		break;
	default :
		wait();
		for(i = 0; i < nmatches+2; i++)
			free(argv[i]);
		free(argv);
		break;
	}

	return error;
}

static void
fsopen(Req *r)
{
	int fd;
	switch(r->ifcall.mode & 1) { // discard OTRUNC etc.
	case OREAD :
		fd = create(tmpnam(nil), ORDWR|ORCLOSE, 0600);
		if(fd < 1)  { // assume fd 0 is taken !
			respond(r, "/tmp/$file create failed");
			return;
		}
		break;
	case OWRITE :
		fd = create(tmpnam(nil), ORDWR|ORCLOSE, 0600);
		if(fd < 1)  { // assume fd 0 is taken !
			respond(r, "/tmp/$file create failed");
			return;
		}
		fd = -fd;
		break;
	default :
		respond(r, "permission denied");
		return;
	}

	r->fid->aux = (void*)fd;


	Path *p = find_path(&r->fid->qid);
	char *error = nil;
	if(fd > 0)
		error = do_action("/bin/action-read", fd, p);
	respond(r, error);
}

static void
remove_path(Path *p) {
	Path *pp;
	pp = find_prev_path(&p->qid);
	if(pp)
		pp->next = p->next;
	else
		root = nil;
	free(p->name);
	free(p);
}

static void
fsclose(Fid *fid) {

	if(fid->aux)
		close(abs((int)fid->aux));

	Path *p = find_path(&fid->qid);
	if(p && p->qid.path)  // p *should* always be non null
		if(--p->qid.vers == 0)
			remove_path(p);
}

static void
fsclunk(Fid *fid) {
	Path *p = find_path(&fid->qid);

	if((int)fid->aux < 0) {
		seek(abs((int)fid->aux), 0, 0);
		do_action("/bin/action-write", (int)fid->aux, p);
	}
	if(fid->aux)
		close(abs((int)fid->aux));

	if(p && p->qid.path)  // p *should* always be non null
		if(--p->qid.vers == 0)
			remove_path(p);
}

static void
fsread(Req *r)
{
	seek((int)r->fid->aux, r->ifcall.offset, 0);
	int k = read((int)r->fid->aux, r->ofcall.data, r->ifcall.count);
	if(k < 0)
		respond(r, "Read failed");
	r->ofcall.count = k;
	respond(r, nil);
}

static void
fswrite(Req *r)
{
	seek(abs((int)r->fid->aux), r->ifcall.offset, 0);
	int k = write(abs((int)r->fid->aux), r->ifcall.data, r->ifcall.count);
	if(k < 0)
		respond(r, "Write failed");
	r->ofcall.count = k;
	respond(r, nil);
}

Srv numsrv = {
.attach=	fsattach,
.walk1=	fswalk1,
.open=	fsopen,
.read=	fsread,
.write=	fswrite,
.stat=	fsstat,
.destroyfid = fsclunk,
};

static int
num_matches(char *txt){
	int i = 0;
	char *p;
	for(p = txt; p ; i++) {
		p = strchr(p, '(');
		if(p) p++;
	}
	return i ? i : 1;
}

extern int chatty9p;

void
main(int argc, char **argv)
{
	char *mtpt, *service;
	char *reg;

	ARGBEGIN{
	case 'D':
		chatty9p++;
		break;
	}ARGEND

	if(argc == 1)
		reg = argv[0];
	else
		reg = ".*";

	nmatches = num_matches(reg);
	freg = regcomp(reg);

	mtpt = "/n/actionfs";
	service = "actionfs";
	
	chdir("/tmp");
	postmountsrv(&numsrv, service, mtpt, MREPL);
	exits(nil);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.