shithub: riscv

Download patch

ref: 8bc1aa612728c19abe8c81e250640a1c33bc03a5
parent: 266e791298a30748fd930c5c460bc4d3b2c7f961
author: cinap_lenrek <cinap_lenrek@localhost>
date: Sun May 15 16:08:42 EDT 2011

add ip/tftpfs

--- a/sys/src/cmd/ip/mkfile
+++ b/sys/src/cmd/ip/mkfile
@@ -18,6 +18,7 @@
 	telnet\
 	telnetd\
 	tftpd\
+	tftpfs\
 	traceroute\
 	udpecho\
 	wol\
--- /dev/null
+++ b/sys/src/cmd/ip/tftpfs.c
@@ -1,0 +1,477 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <auth.h>
+#include <fcall.h>
+#include <9p.h>
+#include <ip.h>
+
+enum {
+	Qdata = 1,
+
+	Tftp_READ	= 1,
+	Tftp_WRITE	= 2,
+	Tftp_DATA	= 3,
+	Tftp_ACK	= 4,
+	Tftp_ERROR	= 5,
+	Tftp_OACK	= 6,
+
+	TftpPort		= 69,
+
+	Segsize		= 512,
+	Maxpath		= 2+2+Segsize-8,
+};
+
+typedef struct Tfile Tfile;
+struct Tfile
+{
+	int		id;
+	uchar	addr[IPaddrlen];
+	char		path[Maxpath];
+	Channel	*c;
+	Tfile		*next;
+	Ref;
+};
+
+uchar ipaddr[IPaddrlen];
+static ulong time0;
+Tfile *files;
+
+
+static Tfile*
+tfileget(uchar *addr, char *path)
+{
+	Tfile *f;
+	static int id;
+
+	for(f = files; f; f = f->next){
+		if(memcmp(addr, f->addr, IPaddrlen) == 0 && strcmp(path, f->path) == 0){
+			incref(f);
+			return f;
+		}
+	}
+	f = emalloc9p(sizeof *f);
+	memset(f, 0, sizeof(*f));
+	ipmove(f->addr, addr);
+	strncpy(f->path, path, sizeof(f->path));
+	f->ref = 1;
+	f->id = id++;
+	f->next = files;
+	files = f;
+
+	return f;
+}
+
+static void
+tfileput(Tfile *f)
+{
+	Channel *c;
+	Tfile **pp;
+
+	if(f==nil || decref(f))
+		return;
+	if(c = f->c){
+		f->c = nil;
+	 	sendp(c, nil);
+	}
+	for(pp = &files; *pp; pp = &(*pp)->next){
+		if(*pp == f){
+			*pp = f->next;
+			break;
+		}
+	}
+	free(f);
+}
+
+static char*
+basename(char *p)
+{
+	char *b;
+
+	for(b = p; *p; p++)
+		if(*p == '/')
+			b = p+1;
+	return b;
+}
+
+static void
+tfilestat(Req *r, char *path, vlong length)
+{
+	memset(&r->d, 0, sizeof(r->d));
+	r->d.uid = estrdup9p("tftp");
+	r->d.gid = estrdup9p("tftp");
+	r->d.name = estrdup9p(basename(path));
+	r->d.atime = r->d.mtime = time0;
+	r->d.length = length;
+	r->d.qid.path = r->fid->qid.path;
+	if(r->fid->qid.path & Qdata){
+		r->d.qid.type = 0;
+		r->d.mode = 0555;
+	} else {
+		r->d.qid.type = QTDIR;
+		r->d.mode = DMDIR|0555;
+	}
+	respond(r, nil);
+}
+
+static void
+catch(void *, char *msg)
+{
+	if(strstr(msg, "alarm"))
+		noted(NCONT);
+	noted(NDFLT);
+}
+
+static int
+newport(void)
+{
+	static int port;
+	return 5000+(port++)%64;
+}
+
+static int
+filereq(uchar *buf, char *path)
+{
+	uchar *p;
+	int n;
+
+	hnputs(buf, Tftp_READ);
+	p = buf+2;
+	n = strlen(path);
+
+	/* hack: remove the trailing dot */
+	if(path[n-1] == '.')
+		n--;
+
+	memcpy(p, path, n);
+	p += n;
+	*p++ = 0;
+	memcpy(p, "octet", 6);
+	p += 6;
+	return p - buf;
+}
+
+static void
+download(void *aux)
+{
+	int fd, cfd, last, block, n, ndata;
+	char *err, addr[40], adir[40];
+	uchar *data;
+	Channel *c;
+	Tfile *f;
+	Req *r;
+
+	struct {
+		Udphdr;
+		uchar buf[2+2+Segsize+1];
+	} msg;
+
+	c = nil;
+	r = nil;
+	fd = cfd = -1;
+	err = nil;
+	data = nil;
+	ndata = 0;
+
+	if((f = aux) == nil)
+		goto out;
+	if((c = f->c) == nil)
+		goto out;
+
+	threadsetname(f->path);
+
+	for(n=0; n<10; n++){
+		snprint(addr, sizeof(addr), "udp!*!%d", newport());
+		if((cfd = announce(addr, adir)) >= 0)
+			break;
+	}
+	if(cfd < 0){
+		err = "announce: %r";
+		goto out;
+	}
+	if(write(cfd, "headers", 7) < 0){
+		err = "write ctl: %r";
+		goto out;
+	}
+	strcat(adir, "/data");
+	if((fd = open(adir, ORDWR)) < 0){
+		err = "open: %r";
+		goto out;
+	}
+
+	n = filereq(msg.buf, f->path);
+	ipmove(msg.raddr, f->addr);
+	hnputs(msg.rport, TftpPort);
+	if(write(fd, &msg, sizeof(Udphdr) + n) < 0){
+		err = "send read request: %r";
+		goto out;
+	}
+
+	notify(catch);
+
+	last = 0;
+	while(!last){
+		alarm(5000);
+		if((n = read(fd, &msg, sizeof(Udphdr) + sizeof(msg.buf)-1)) < 0){
+			err = "receive response: %r";
+			goto out;
+		}
+		alarm(0);
+
+		n -= sizeof(Udphdr);
+		msg.buf[n] = 0;
+		switch(nhgets(msg.buf)){
+		case Tftp_ERROR:
+			werrstr((char*)msg.buf+4);
+			err = "%r";
+			goto out;
+
+		case Tftp_DATA:
+			if(n < 4)
+				continue;
+			block = nhgets(msg.buf+2);
+			if((n -= 4) > 0){
+				data = erealloc9p(data, ndata + n);
+				memcpy(data + ndata, msg.buf+4, n);
+				ndata += n;
+
+rloop:			/* hanlde read request while downloading */
+				if((r != nil) && (r->ifcall.type == Tread) && (r->ifcall.offset < ndata)){
+					readbuf(r, data, ndata);
+					respond(r, nil);
+					r = nil;
+				}
+				if((r == nil) && (nbrecv(c, &r) == 1)){
+					if(r == nil){
+						chanfree(c);
+						c = nil;
+						goto out;
+					}
+					goto rloop;
+				}
+			}
+			if(n < Segsize)
+				last = 1;
+			hnputs(msg.buf, Tftp_ACK);
+			hnputs(msg.buf+2, block);
+			if(write(fd, &msg, sizeof(Udphdr) + 4) < 0){
+				err = "send acknowledge: %r";
+				goto out;
+			}
+			break;
+		}
+	}
+
+out:
+	alarm(0);
+	if(cfd >= 0)
+		close(cfd);
+	if(fd >= 0)
+		close(fd);
+
+	if(c){
+		while((r != nil) || (r = recvp(c))){
+			if(err){
+				char buf[ERRMAX];
+
+				snprint(buf, sizeof(buf), err);
+				respond(r, buf);
+			} else {
+				switch(r->ifcall.type){
+				case Tread:
+					readbuf(r, data, ndata);
+					respond(r, nil);
+					break;
+				case Tstat:
+					tfilestat(r, f->path, ndata);
+					break;
+				default:
+					respond(r, "bug in fs");
+				}
+			}
+			r = nil;
+		}
+		chanfree(c);
+	}
+	free(data);
+}
+
+static void
+fsattach(Req *r)
+{
+	Tfile *f;
+
+	if(r->ifcall.aname && r->ifcall.aname[0]){
+		uchar addr[IPaddrlen];
+
+		if(parseip(addr, r->ifcall.aname) < 0){
+			respond(r, "bad ip specified");
+			return;
+		}
+		f = tfileget(addr, "/");
+	} else {
+		if(ipcmp(ipaddr, IPnoaddr) == 0){
+			respond(r, "no ipaddr specified");
+			return;
+		}
+		f = tfileget(ipaddr, "/");
+	}
+	r->fid->aux = f;
+	r->fid->qid.type = QTDIR;
+	r->fid->qid.path = f->id<<1;
+	r->fid->qid.vers = 0;
+	r->ofcall.qid = r->fid->qid;
+	respond(r, nil);
+}
+
+static char*
+fswalk1(Fid *fid, char *name, Qid *qid)
+{
+	Tfile *f;
+	char *t;
+
+	f = fid->aux;
+	t = smprint("%s/%s", f->path, name);
+	f = tfileget(f->addr, cleanname(t));
+	free(t);
+	tfileput(fid->aux); fid->aux = f;
+	fid->qid.type = QTDIR;
+	fid->qid.path = f->id<<1;
+
+	/* hack:
+	 * a dot in the path means the path element is not
+	 * a directory. to force download of files containing
+	 * no dot, a trailing dot can be appended that will
+	 * be stripped out in the tftp read request.
+	 */
+	if(strchr(f->path, '.') != nil){
+		fid->qid.type = 0;
+		fid->qid.path |= Qdata;
+	}
+
+	if(qid)
+		*qid = fid->qid;
+	return nil;
+}
+
+static char*
+fsclone(Fid *oldfid, Fid *newfid)
+{
+	Tfile *f;
+
+	f = oldfid->aux;
+	incref(f);
+	newfid->aux = f;
+	return nil;
+}
+
+static void
+fsdestroyfid(Fid *fid)
+{
+	tfileput(fid->aux);
+	fid->aux = nil;
+}
+
+static void
+fsopen(Req *r)
+{
+	int m;
+
+	m = r->ifcall.mode & 3;
+	if(m != OREAD && m != OEXEC){
+		respond(r, "permission denied");
+		return;
+	}
+	respond(r, nil);
+}
+
+static void
+dispatch(Req *r)
+{
+	Tfile *f;
+
+	f = r->fid->aux;
+	if(f->c == nil){
+		f->c = chancreate(sizeof(r), 0);
+		proccreate(download, f, 16*1024);
+	}
+	sendp(f->c, r);
+}
+
+static void
+fsread(Req *r)
+{
+	if(r->fid->qid.path & Qdata){
+		dispatch(r);
+	} else {
+		respond(r, nil);
+	}
+}
+
+static void
+fsstat(Req *r)
+{
+	if(r->fid->qid.path & Qdata){
+		dispatch(r);
+	} else {
+		tfilestat(r, ((Tfile*)r->fid->aux)->path, 0);
+	}
+}
+
+Srv fs = 
+{
+.attach=		fsattach,
+.destroyfid=	fsdestroyfid,
+.walk1=		fswalk1,
+.clone=		fsclone,
+.open=		fsopen,
+.read=		fsread,
+.stat=		fsstat,
+};
+
+void
+usage(void)
+{
+	fprint(2, "usage: tftpfs [-D] [-s srvname] [-m mtpt] [ipaddr]\n");
+	threadexitsall("usage");
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	char *srvname = nil;
+	char *mtpt = nil;
+
+	time0 = time(0);
+	ipmove(ipaddr, IPnoaddr);
+
+	ARGBEGIN{
+	case 'D':
+		chatty9p++;
+		break;
+	case 's':
+		srvname = EARGF(usage());
+		break;
+	case 'm':
+		mtpt = EARGF(usage());
+		break;
+	default:
+		usage();
+	}ARGEND;
+
+	switch(argc){
+	case 0:
+		break;
+	case 1:
+		if(parseip(ipaddr, *argv) < 0)
+			usage();
+		break;
+	default:
+		usage();
+	}
+
+	if(srvname==nil && mtpt==nil)
+		usage();
+
+	threadpostmountsrv(&fs, srvname, mtpt, MREPL|MCREATE);
+}