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);
+}