shithub: ridefs

Download patch

ref: 8eef000ed6e16e02354e6798f227223cc38b2d42
author: B. Wilson <x@wilsonb.com>
date: Mon Jun 16 12:30:48 EDT 2025

Initial PoC

--- /dev/null
+++ b/mkfile
@@ -1,0 +1,7 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin
+TARG=ridefs
+OFILES=ridefs.$O
+
+</sys/src/cmd/mkone
\ No newline at end of file
--- /dev/null
+++ b/ridefs.c
@@ -1,0 +1,780 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include <stdio.h>
+#include <json.h>
+
+
+typedef struct Client Client;
+typedef struct Rfid Rfid;
+
+struct Client {
+	Ref;
+
+	char *addr;
+	ulong umask;
+	int timeout;
+
+	/* internal use */
+	char *user;
+	char *pres;  /* previous result */
+	long time0;
+	int id;
+	int oio;     /* io opened */
+	int iopid;   /* read/write fork */
+	int fd;      /* data */
+	int cfd;     /* ctl */
+
+	/* id reply info */
+	int Rapiversion;
+	int Rport;
+	int Rpid;
+	char *Ripaddress;
+	char *Rvendor;
+	char *Rlanguage;
+	char *Rversion;
+	char *Rmachine;
+	char *Rarch;
+	char *Rproject;
+	char *Rprocess;
+	char *Ruser;
+	char *Rtoken;
+	char *Rdate;
+	char *Rplatform;
+};
+
+enum {
+	Qroot,
+		Qrctl,
+		Qclone,
+		Qclient,
+			Qctl,
+			Qio,
+	QCOUNT
+};
+
+static char *nametab[] = {
+	"/",
+		"ctl",
+		"clone",
+		nil,
+			"ctl",
+			"io",
+};
+
+struct Rfid {
+	Ref;
+	int kind;
+	int client;
+};
+
+#define RIDESRV_VERS 0
+static Client *cpool;
+static char *mtpt;
+static char *service;
+static char *net;
+static char *user;
+static ulong umask;
+static long bufsz;
+static long time0;
+static uint nclients;
+static int debug;
+static int timeout;
+
+void*
+ecalloc(ulong n){
+	void *p;
+
+	p = emalloc9p(n);
+	setmalloctag(p, getcallerpc(&n));
+	memset(p, 0, n);
+
+	return p;
+}
+
+void*
+erealloc(void *p, ulong n){
+	p = erealloc9p(p, n);
+	setrealloctag(p, getcallerpc(&n));
+	return p;
+}
+
+char*
+estrdup(char *s){
+	s = estrdup9p(s);
+	setrealloctag(s, getcallerpc(&s));
+	return s;
+}
+
+int
+mkclient(void){
+	Client *c;
+	int i;
+
+	for(i = 0; i < nclients; i++)
+		if(cpool[i].ref == 0)
+			break;
+	if(i == nelem(cpool))
+		return -1;
+	c = &cpool[i];
+
+	incref(c);
+	c->id = i;
+	c->timeout = timeout;
+	c->umask = umask;
+	c->user = estrdup(getuser());
+	c->time0 = time(0);
+
+	return i;
+}
+
+Client*
+clientref(int i){
+	if(i < 0 || i > nclients)
+		return nil;
+
+	return &cpool[i];
+}
+
+void
+rmclient(int i){
+	Client *c;
+
+	c = clientref(i);
+	if(c == nil || decref(c))
+		return;
+
+	if(c->Ripaddress) free(c->Ripaddress);
+	if(c->Rvendor) free(c->Rvendor);
+	if(c->Rlanguage) free(c->Rlanguage);
+	if(c->Rversion) free(c->Rversion);
+	if(c->Rmachine) free(c->Rmachine);
+	if(c->Rarch) free(c->Rarch);
+	if(c->Rproject) free(c->Rproject);
+	if(c->Rprocess) free(c->Rprocess);
+	if(c->Ruser) free(c->Ruser);
+	if(c->Rtoken) free(c->Rtoken);
+	if(c->Rdate) free(c->Rdate);
+	if(c->Rplatform) free(c->Rplatform);
+
+	if(c->user) free(c->user);
+	if(c->fd) close(c->fd);
+	if(c->cfd) close(c->cfd);
+
+	memset(c, 0, sizeof(*c));
+}
+
+static void
+mkqid(Qid *q, int k, Client *c){
+	q->vers = RIDESRV_VERS;
+	q->path = ((u64int)c->id<<32) | k&0xffffffff;
+
+	switch(k){
+	case Qroot:
+	case Qclient:
+		q->type = QTDIR; break;
+	default:
+		q->type = QTFILE;
+	}
+}
+
+long
+writemsg(int fd, void *pld, long n){
+	long len;
+	char *r;
+
+	len = n+8;
+	r = ecalloc(len+8);
+	r[0] = 24>>len & 0xff;
+	r[1] = 16>>len & 0xff;
+	r[2] =  8>>len & 0xff;
+	r[3] =     len & 0xff;
+	r[4] = 'R';
+	r[5] = 'I';
+	r[6] = 'D';
+	r[7] = 'E';
+	memcpy(&r[8], pld, n);
+	len = write(fd, r, len);
+
+	return len;
+}
+
+long
+readmsg(int fd, char *pld, long n){
+	int len;
+	char buf[9];
+
+	if(0 > readn(fd, buf, 8))
+		return -1;
+	buf[9] = '\0';
+	if(0 != strcmp(&buf[4], "RIDE"))
+		return -2;
+	len = -8 + (buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3]);
+	if(len > n)
+		return -3;
+	if(0 > readn(fd, pld, len))
+		return -4;
+
+	pld = erealloc(pld, len+1);
+	pld[len] = '\0';
+	return len;
+}
+
+char *
+rideinit(int client){
+	int fd;
+	char *addr, *pld, *s;
+	Client *c;
+	JSON *j;
+	JSONEl *d;
+
+	c = clientref(client);
+	addr = netmkaddr(c->addr, net, "tcp");
+	if((fd = dial(addr, nil, nil, &c->cfd)) < 0)
+		return "failed to dial addr";
+	c = clientref(client);
+	c->fd = fd;
+
+	pld = ecalloc(bufsz);
+	if(0 > readmsg(fd, pld, bufsz))
+		return "failed to read handshake";
+	if(0 != strcmp(pld, "SupportedProtocols=2"))
+		return "unrecognized protocol";
+	free(pld);
+
+	pld = "UsingProtocol=2";
+	if(0 > writemsg(fd, pld, strlen(pld)))
+		return "failed to write handshake";
+		
+	pld = "[\"Identify\",{\"apiVersion\":1,\"identity\":1}]";
+	if(0 > writemsg(fd, pld, strlen(pld)))
+		return "failed to send identification message";
+
+	pld = ecalloc(bufsz);
+	if(0 > readmsg(fd, pld, bufsz))
+		return "failed to receive identification message";
+	j = jsonparse(pld);
+	free(pld);
+	if(j == nil || j->t != JSONArray || nil == j->first)
+		return "unrecognized reply";
+	if(nil == (s = jsonstr(j->first->val)) || 0 != strcmp(s, "ReplyIdentify"))
+		return "unexpected identification reply";
+	if(nil == (d = j->first->next) || d->val->t != JSONObject)
+		return "malformed identification reply";
+
+	c->Rapiversion = jsonbyname(d->val, "apiVersion")->n;
+	c->Rport       = jsonbyname(d->val, "Port")->n;
+	c->Rpid        = jsonbyname(d->val, "pid")->n;
+	c->Ripaddress  = estrdup(jsonstr(jsonbyname(d->val, "IPAddress")));
+	c->Rvendor     = estrdup(jsonstr(jsonbyname(d->val, "Vendor")));
+	c->Rlanguage   = estrdup(jsonstr(jsonbyname(d->val, "Language")));
+	c->Rversion    = estrdup(jsonstr(jsonbyname(d->val, "version")));
+	c->Rmachine    = estrdup(jsonstr(jsonbyname(d->val, "Machine")));
+	c->Rarch       = estrdup(jsonstr(jsonbyname(d->val, "arch")));
+	c->Rproject    = estrdup(jsonstr(jsonbyname(d->val, "Project")));
+	c->Rprocess    = estrdup(jsonstr(jsonbyname(d->val, "Process")));
+	c->Ruser       = estrdup(jsonstr(jsonbyname(d->val, "User")));
+	c->Rtoken      = estrdup(jsonstr(jsonbyname(d->val, "token")));
+	c->Rdate       = estrdup(jsonstr(jsonbyname(d->val, "date")));
+	c->Rplatform   = estrdup(jsonstr(jsonbyname(d->val, "platform")));
+
+	jsonfree(j);
+	return nil;
+}
+
+
+long
+readqrctl(char **buf){
+	char *b;
+
+	b = ecalloc(bufsz);
+	sprintf(b,
+		"version %i\n"
+		"bufsz %u\n"
+		"nclients %u\n"
+		"debug %i\n"
+		"timeout %i\n"
+		"umask %o\n",
+		RIDESRV_VERS, bufsz, nclients,
+		debug, timeout, umask);
+
+	*buf = b;
+	return strlen(b);
+}
+
+long
+writeqrctl(char *b, long n){
+	char *s;
+	char *sep = " 	";
+
+	for(s = strtok(b, sep); s != nil; s = strtok(nil, sep)){
+		if(strcmp(s, "bufsz") == 0)
+			bufsz = strtoul(strtok(nil, sep), nil, 0);
+		if(strcmp(s, "umask") == 0)
+			umask = strtoul(strtok(nil, sep), nil, 0);
+		if(strcmp(s, "nclients") == 0)
+			nclients = strtoul(strtok(nil, sep), nil, 0);
+		if(strcmp(s, "debug") == 0)
+			debug = atoi(strtok(nil, sep));
+		if(strcmp(s, "timeout") == 0)
+			timeout = atoi(strtok(nil, sep));
+	}
+
+	cpool = erealloc(cpool, nclients*sizeof(*cpool));
+
+	return n;
+}
+
+long
+readqctl(char **buf, Client *c){
+	char *b, r[1024];
+
+	b = ecalloc(bufsz);
+	sprintf(b,
+		"connect %s\n",
+		"timeout %i\n",
+		"umask %l",
+		c->addr, c->timeout, c->umask);
+	if(c->oio){
+		sprintf(r,
+			"Rapiversion %i\n"
+			"Rport %i\n"
+			"Rpid %i\n"
+			"Ripaddress %s\n"
+			"Rvendor %s\n"
+			"Rlanguage %s\n"
+			"Rversion %s\n"
+			"Rmachine %s\n"
+			"Rarch %s\n"
+			"Rproject %s\n"
+			"Rprocess %s\n"
+			"Ruser %s\n"
+			"Rtoken %s\n"
+			"Rdate %s\n"
+			"Rplatform %s\n",
+			c->Rapiversion, c->Rport, c->Rpid, c->Ripaddress,
+			c->Rvendor, c->Rlanguage, c->Rversion, c->Rmachine,
+			c->Rarch, c->Rproject, c->Rprocess, c->Ruser,
+			c->Rtoken, c->Rdate, c->Rplatform);
+		strcpy(b, r);
+	}
+
+	*buf = b;
+	return strlen(b);
+}
+
+long
+writeqctl(char *b, long n, Client *c){
+	char *s, *sep;
+
+	sep = " 	";
+	for(s = strtok(b, sep); s != nil; s = strtok(nil, sep)){
+		if(strcmp(s, "connect") == 0)
+			c->addr = estrdup(strtok(nil, sep));
+		if(strcmp(s, "timeout") == 0)
+			c->timeout = atoi(strtok(nil, sep));
+		if(strcmp(s, "umask") == 0)
+			c->umask = strtoul(strtok(nil, sep), nil, 0);
+	}
+
+	return n;
+}
+
+long
+readqio(char **buf, Client *c){
+	*buf = estrdup(c->pres);
+	return strlen(*buf);
+}
+
+long
+writeqio(char *b, long, Client *c){
+	char *pld, *e, *p, *z;
+	long sz;
+
+	pld = z = ecalloc(bufsz);
+	e = pld + bufsz - 1;
+	sz = -1;
+	if(p == (z = strecpy(p = z, e, "[\"Execute\",{\"text\":\"      ")))
+		goto end;
+	if(p == (z = strecpy(p = z, e, b)))
+		goto end;
+	if(p == strecpy(p = z, e, "\",\"trace\":0}]"))
+		goto end;
+
+	sz = strlen(pld);
+	writemsg(c->fd, pld, sz);
+	readmsg(c->fd, pld, bufsz);
+	c->pres = estrdup(pld);
+
+	end:
+	free(pld);
+	return sz;
+}
+
+void
+mkdirent(Dir *d, int kind, Client *c){
+	char *nm, *buf;
+
+	mkqid(&d->qid, kind, c);
+	d->mode = 0444 & umask;
+	if(nil != (nm = nametab[kind]))
+		d->name = nm;
+
+	buf = ecalloc(bufsz);
+	switch(kind){
+	case Qroot:
+		d->mode = 0777 & umask;
+	case Qrctl:
+		d->atime = d->mtime = time0;
+		d->uid = estrdup(user);
+		d->gid = estrdup(user);
+		d->muid = estrdup(user);
+		break;
+	case Qclient:
+		d->mode = 0777 & c->umask;
+		sprintf(buf, "%i", c->id);
+		d->name = estrdup(buf);
+	default:
+		d->atime = d->mtime = c->time0;
+		d->uid = estrdup(c->user);
+		d->gid = estrdup(c->user);
+		d->muid = estrdup(c->user);
+	}
+
+	switch(kind){
+	case Qrctl: d->length = readqrctl(&buf);
+	case Qctl: d->length = readqctl(&buf, c);
+	case Qio: d->length = readqio(&buf, c);
+	}
+
+	free(buf);
+}
+
+int
+genqroot(int i, Dir *d, void*){
+	static int n;
+	int j;
+
+	i += Qroot + 1;
+	if(i < Qclient){
+		mkdirent(d, i, nil);
+	} else {
+		i -= Qclient;
+		if(i == 0)
+			n = 0;
+		for(j = n; j < nclients && cpool[j].ref == 0; j++);
+		if(j == nclients)
+			return -1;
+		n++;
+		mkdirent(d, Qclient, clientref(j));
+	}
+
+	return 0;
+}
+
+int
+genqclient(int i, Dir *d, void *aux){
+	Client *c;
+
+	c = aux;
+	i += Qclient + 1;
+	if(i >= QCOUNT)
+		return -1;
+	mkdirent(d, i, c);
+	return 0;
+}
+
+
+static void
+fsdestroyfid(Fid *fid){
+	Rfid *f;
+
+	f = fid->aux;
+	if(-1 < f->client){
+		rmclient(f->client);
+		free(f);
+	}
+}
+
+static void
+fsstart(Srv*){
+	if(mtpt != nil)
+		unmount(nil, mtpt);
+}
+
+static void
+fsend(Srv*){
+	postnote(PNGROUP, getpid(), "shutdown");
+	exits(nil);
+}
+
+static void
+fsattach(Req *r){
+	Rfid *f;
+	
+	f = ecalloc(sizeof(*f));
+	f->kind = Qroot;
+
+	mkqid(&r->fid->qid, f->kind, nil);
+	r->fid->aux = f;
+	r->ofcall.qid = r->fid->qid;
+
+	respond(r, nil);
+}
+
+static void
+fsopen(Req *r){
+	int e, pid;
+	char *res;
+	Rfid *f;
+	Client *c;
+
+	f = r->fid->aux;
+	c = clientref(f->client);
+
+	switch(f->kind){
+	case Qclone:
+		if((f->client = mkclient()) == -1){
+			respond(r, "reached client limit");
+			return;
+		}
+
+		f->kind = Qctl;
+		c = clientref(f->client);
+
+		mkqid(&r->ofcall.qid, f->kind, c);
+		r->fid->qid = r->ofcall.qid;
+
+		respond(r, nil);
+		break;
+	case Qio:
+		if(e = c->oio)
+			respond(r, "client in use");
+		else if(e = (nil == c->addr))
+			respond(r, "no server set");
+		if(e)
+			return;
+
+		switch(pid = rfork(RFPROC|RFNOWAIT|RFMEM)){
+		case 0:
+			alarm(timeout);
+			res = rideinit(f->client);
+			alarm(0);
+			if(res == nil)
+				c->oio = 1;
+			c->iopid = 0;
+			respond(r, res);
+			break;
+		case -1:
+			respond(r, "failed to init ride");
+			break;
+		default:
+			c->iopid = pid;
+		}
+		break;
+	}
+}
+
+static void
+fsread(Req *r){
+	Rfid *f;
+	Client *c;
+	char *buf;
+	long n;
+
+	buf = nil;
+	n = -1;
+	f = r->fid->aux;
+	c = clientref(f->client);
+	switch(f->kind){
+	case Qroot: dirread9p(r, genqroot, nil); break;
+	case Qrctl: n = readqrctl(&buf); break;
+	case Qclone: respond(r, "read prohibited"); return;
+	case Qclient: dirread9p(r, genqclient, c); break;
+	case Qctl: n = readqctl(&buf, c); break;
+	case Qio: n = readqio(&buf, c); break;
+	}
+
+	if(buf != nil){
+		readbuf(r, buf, n);
+		free(buf);
+	}
+
+	respond(r, nil);
+}
+
+static void
+fswrite(Req *r){
+	Rfid *f;
+	Client *c;
+	int pid;
+
+	f = r->fid->aux;
+	c = clientref(f->client);
+
+	switch(f->kind){
+	case Qrctl:
+		r->ofcall.count = writeqrctl(r->ifcall.data, r->ifcall.count);
+		break;
+	case Qctl:
+		r->ofcall.count = writeqctl(r->ifcall.data, r->ifcall.count, c);
+		break;
+	case Qio:
+		switch(pid = rfork(RFPROC|RFNOWAIT|RFMEM)){
+		case 0:
+			alarm(c->timeout);
+			r->ofcall.count = writeqio(r->ifcall.data, r->ifcall.count, c);
+			alarm(0);
+			c->iopid = 0;
+			respond(r, nil);
+			break;
+		case -1:
+			respond(r, "failed to send command");
+			break;
+		default:
+			c->iopid = pid;
+		}
+		break;
+	default:
+		respond(r, "write prohibited"); return;
+	}
+}
+
+static void
+fsflush(Req *r){
+	Rfid *f;
+	Client *c;
+
+	f = r->fid->aux;
+	c = clientref(f->client);
+
+	if(0 < c->iopid){
+		postnote(PNPROC, c->iopid, "interrupt");
+		respond(r, "interrupted");
+	}
+
+	respond(r, nil);
+}
+
+static void
+fsstat(Req *r){
+	Rfid *f;
+	Client *c;
+
+	f = r->fid->aux;
+	c = clientref(f->client);
+	mkdirent(&r->d, f->kind, c);
+
+	respond(r, nil);
+}
+
+static char*
+fswalk1(Fid *fid, char *name, Qid *qid){
+	Rfid *f;
+	Client *c;
+	int i, n;
+	char *nend;
+
+	if(!(fid->qid.type&QTDIR))
+		return "cannot walk from non-directory";
+
+	f = fid->aux;
+	n = -1;
+	if(strcmp(name, "..") == 0){
+		switch(f->kind){
+		case Qroot:
+			break;
+		case Qclient:
+			rmclient(f->client);
+			f->client = -1;
+			break;
+		default:
+			if(f->kind > Qclient)
+				f->kind = Qclient;
+		}
+	} else {
+		for(i = f->kind+1; i<QCOUNT; i++){
+			if(nametab[i] && strcmp(name, nametab[i]) == 0)
+				break;
+			if(i == Qclient){
+				n = strtol(name, &nend, 10);
+				if(*nend == 0 && nil != (c = clientref(n)) && c->ref != 0){
+					f->client = n;
+					incref(c);
+					break;
+				}	
+			}
+		}
+		if(i >= QCOUNT)
+			return "directory entry not found";
+		f->kind = i;
+	}
+	mkqid(qid, f->kind, clientref(n));
+	fid->qid = *qid;
+	return nil;
+}
+
+static char*
+fsclone(Fid *oldfid, Fid *newfid){
+	Rfid *f, *o;
+
+	o = oldfid->aux;
+	if(o == nil)
+		return "bad fid";
+
+	f = ecalloc(sizeof(*f));
+	memmove(f, o, sizeof(*f));
+
+	if(-1 < f->client)
+		incref(clientref(f->client));
+	newfid->aux = f;
+
+	return nil;
+}
+
+Srv fs = {
+	.destroyfid = fsdestroyfid,
+	.start      = fsstart,
+	.end        = fsend,
+	.attach     = fsattach,
+	.open       = fsopen,
+	.read       = fsread,
+	.write      = fswrite,
+	.flush      = fsflush,
+	.stat       = fsstat,
+	.walk1      = fswalk1,
+	.clone      = fsclone,
+};
+
+void
+usage(void){
+	fprintf(stderr, "usage: %s [-Dd] [-T timeout] [-m mtpt] [-s service] [-x net]\n", argv0);
+}
+
+void
+main(int argc, char **argv){
+	timeout = 10000;
+	mtpt = "/mnt/ride";
+	bufsz = 4096;
+	umask = 0755;
+	time0 = time(0);
+	nclients = 256;
+
+	ARGBEGIN{
+	case 'D': chatty9p++; break;
+	case 'd': debug++; break;
+	case 'T': timeout = atoi(EARGF(usage()));
+	case 'm': mtpt = EARGF(usage()); break;
+	case 's': service = EARGF(usage()); break;
+	case 'x': net = EARGF(usage()); break;
+	default:  usage(); return;
+	}ARGEND
+
+	cpool = ecalloc(nclients*sizeof(*cpool));
+
+	rfork(RFNOTEG);
+	postmountsrv(&fs, service, mtpt, MREPL);
+	exits(nil);
+}
\ No newline at end of file
--