2 * SSH network file system.
3 * Presents remote TCP stack as /net-style file system.
14 typedef struct Client Client;
15 typedef struct Msg Msg;
31 #define PATH(type, n) ((type)|((n)<<8))
32 #define TYPE(path) ((int)(path) & 0xFF)
33 #define NUM(path) ((uint)(path)>>8)
35 Channel *ssherrchan; /* chan(char*) */
36 Channel *sshmsgchan; /* chan(Msg*) */
37 Channel *fsreqchan; /* chan(Req*) */
38 Channel *fsreqwaitchan; /* chan(nil) */
39 Channel *fsclunkchan; /* chan(Fid*) */
40 Channel *fsclunkwaitchan; /* chan(nil) */
82 MSG_CHANNEL_OPEN = 90,
83 MSG_CHANNEL_OPEN_CONFIRMATION,
84 MSG_CHANNEL_OPEN_FAILURE,
85 MSG_CHANNEL_WINDOW_ADJUST,
87 MSG_CHANNEL_EXTENDED_DATA,
107 uchar buf[MaxPacket];
110 #define PUT4(p, u) (p)[0] = (u)>>24, (p)[1] = (u)>>16, (p)[2] = (u)>>8, (p)[3] = (u)
111 #define GET4(p) (u32int)(p)[3] | (u32int)(p)[2]<<8 | (u32int)(p)[1]<<16 | (u32int)(p)[0]<<24
118 char localip[] = "::";
121 vpack(uchar *p, int n, char *fmt, va_list a)
123 uchar *p0 = p, *e = p+n;
133 if(++p > e) goto err;
136 *va_arg(a, void**) = p;
140 *p++ = va_arg(a, int);
144 s = va_arg(a, void*);
147 if(p+4 > e) goto err;
150 if(u > e-p) goto err;
156 if(p+4 > e) goto err;
166 vunpack(uchar *p, int n, char *fmt, va_list a)
168 uchar *p0 = p, *e = p+n;
177 if(++p > e) goto err;
180 *va_arg(a, void**) = p;
184 *va_arg(a, int*) = *p++;
187 if(p+4 > e) goto err;
189 if(u > e-p) goto err;
190 *va_arg(a, void**) = p;
191 *va_arg(a, int*) = u;
195 s = va_arg(a, void*);
197 if(u > e-p) goto err;
202 if(p+4 > e) goto err;
204 *va_arg(a, int*) = u;
218 m = emalloc9p(sizeof(Msg));
220 m->rp = m->wp = m->buf;
221 m->ep = m->rp + sizeof(m->buf);
226 pack(Msg *m, char *fmt, ...)
234 n = vpack(m->wp, m->ep - m->wp, fmt, a);
236 sysfatal("pack faild");
243 unpack(Msg *m, char *fmt, ...)
249 n = vunpack(m->rp, m->wp - m->rp, fmt, a);
265 if(write(sshfd, m->rp, n) != n)
266 sysfatal("write to ssh failed: %r");
277 for(i=0; i<nclient; i++)
278 if(client[i]->ref==0 && client[i]->state == Closed)
282 client = erealloc9p(client, (nclient+16)*sizeof(client[0]));
284 c = emalloc9p(sizeof(Client));
285 memset(c, 0, sizeof(*c));
287 client[nclient++] = c;
294 if(num < 0 || num >= nclient)
300 adjustwin(Client *c, int len)
303 if(c->recvacc >= MaxPacket*WinPackets/2 || c->recvwin < MaxPacket){
304 sendmsg(pack(nil, "buu", MSG_CHANNEL_WINDOW_ADJUST, c->servernum, c->recvacc));
311 senddata(Client *c, void *data, int len)
313 sendmsg(pack(nil, "bus", MSG_CHANNEL_DATA, c->servernum, (char*)data, len));
318 queuerreq(Client *c, Req *r)
324 c->erq = (Req**)&r->aux;
328 queuermsg(Client *c, Msg *m)
334 c->emq = (Msg**)&m->link;
338 matchrmsgs(Client *c)
344 while(c->rq != nil && c->mq != nil){
351 if(n >= m->wp - m->rp){
356 memmove(r->ofcall.data, m->rp, n);
368 queuewreq(Client *c, Req *r)
374 c->ewq = (Req**)&r->aux;
383 while((r = c->wq) != nil && (n = c->sendwin) > 0){
386 if(r->ifcall.count > n){
387 senddata(c, r->ifcall.data, n);
388 r->ifcall.count -= n;
389 memmove(r->ifcall.data, (char*)r->ifcall.data + n, r->ifcall.count);
392 c->wq = (Req*)r->aux;
394 senddata(c, r->ifcall.data, r->ifcall.count);
395 r->ofcall.count = r->ifcall.count;
401 findreq(Client *c, Req *r)
405 for(l=&c->rq; *l; l=(Req**)&(*l)->aux){
413 for(l=&c->wq; *l; l=(Req**)&(*l)->aux){
425 dialedclient(Client *c)
431 sysfatal("more than one outstanding dial request (BUG)");
432 if(c->state == Established)
435 respond(r, "connect failed");
441 teardownclient(Client *c)
444 sendmsg(pack(nil, "bu", MSG_CHANNEL_EOF, c->servernum));
448 hangupclient(Client *c)
454 for(m=c->mq; m; m=mnext){
459 for(r=c->rq; r; r=next){
461 respond(r, "hangup on network connection");
464 for(r=c->wq; r; r=next){
466 respond(r, "hangup on network connection");
472 closeclient(Client *c)
479 if(c->rq != nil || c->wq != nil)
480 sysfatal("ref count reached zero with requests pending (BUG)");
482 for(m=c->mq; m; m=next){
488 if(c->state != Closed)
501 n = read(sshfd, m->rp, m->ep - m->rp);
503 sysfatal("eof on ssh connection");
505 sendp(sshmsgchan, m);
509 typedef struct Tab Tab;
531 fillstat(Dir *d, uvlong path)
535 memset(d, 0, sizeof(*d));
536 d->uid = estrdup9p("ssh");
537 d->gid = estrdup9p("ssh");
539 d->atime = d->mtime = time0;
540 t = &tab[TYPE(path)];
542 d->name = estrdup9p(t->name);
544 d->name = smprint("%ud", NUM(path));
546 sysfatal("out of memory");
548 d->qid.type = t->mode>>24;
555 if(r->ifcall.aname && r->ifcall.aname[0]){
556 respond(r, "invalid attach specifier");
559 r->fid->qid.path = PATH(Qroot, 0);
560 r->fid->qid.type = QTDIR;
561 r->fid->qid.vers = 0;
562 r->ofcall.qid = r->fid->qid;
569 fillstat(&r->d, r->fid->qid.path);
574 rootgen(int i, Dir *d, void*)
585 tcpgen(int i, Dir *d, void*)
594 fillstat(d, PATH(Qn, i));
601 clientgen(int i, Dir *d, void *aux)
608 fillstat(d, PATH(i, c->num));
615 fswalk1(Fid *fid, char *name, Qid *qid)
621 path = fid->qid.path;
622 if(!(fid->qid.type&QTDIR))
623 return "walk in non-directory";
625 if(strcmp(name, "..") == 0){
628 qid->path = PATH(Qtcp, NUM(path));
629 qid->type = tab[Qtcp].mode>>24;
632 qid->path = PATH(Qroot, 0);
633 qid->type = tab[Qroot].mode>>24;
638 return "bug in fswalk1";
643 for(; i<nelem(tab); i++){
646 snprint(buf, sizeof buf, "%d", n);
647 if(n < nclient && strcmp(buf, name) == 0){
648 qid->path = PATH(i, n);
649 qid->type = tab[i].mode>>24;
654 if(strcmp(name, tab[i].name) == 0){
655 qid->path = PATH(i, NUM(path));
656 qid->type = tab[i].mode>>24;
659 if(tab[i].mode&DMDIR)
662 return "directory entry not found";
665 typedef struct Cs Cs;
682 n = strtol(p, &s, 0);
687 db = ndbopen("/lib/ndb/common");
692 port = ndbgetvalue(db, nil, "tcp", p, "port", nil);
708 respond(r, "cs read without write");
711 if(r->ifcall.offset==0){
719 readstr(r, cs->resp);
727 char err[ERRMAX], *f[4], *s, *ns;
731 s = emalloc9p(r->ifcall.count+1);
732 memmove(s, r->ifcall.data, r->ifcall.count);
733 s[r->ifcall.count] = '\0';
735 nf = getfields(s, f, nelem(f), 0, "!");
738 respond(r, "can't translate");
741 if(strcmp(f[0], "tcp") != 0 && strcmp(f[0], "net") != 0){
743 respond(r, "unknown protocol");
746 port = ndbfindport(f[2]);
749 respond(r, "no translation found");
753 ns = smprint("%s/tcp/clone %s!%d", mtpt, f[1], port);
756 rerrstr(err, sizeof err);
764 r->ofcall.count = r->ifcall.count;
769 ctlread(Req *r, Client *c)
773 sprint(buf, "%d", c->num);
779 ctlwrite(Req *r, Client *c)
784 s = emalloc9p(r->ifcall.count+1);
785 r->ofcall.count = r->ifcall.count;
786 memmove(s, r->ifcall.data, r->ifcall.count);
787 s[r->ifcall.count] = '\0';
789 nf = tokenize(s, f, 3);
796 if(strcmp(f[0], "hangup") == 0){
797 if(c->state != Established)
803 }else if(strcmp(f[0], "connect") == 0){
804 if(c->state != Closed)
809 c->connect = estrdup9p(f[1]);
810 nf = getfields(f[1], f, nelem(f), 0, "!");
813 c->sendwin = MaxPacket;
814 c->recvwin = WinPackets * MaxPacket;
819 sendmsg(pack(nil, "bsuuususu", MSG_CHANNEL_OPEN,
821 c->num, c->recvwin, MaxPacket,
822 f[0], strlen(f[0]), ndbfindport(f[1]),
823 localip, strlen(localip), localport));
826 respond(r, "bad or inappropriate tcp control message");
832 dataread(Req *r, Client *c)
834 if(c->state != Established){
835 respond(r, "not connected");
843 datawrite(Req *r, Client *c)
845 if(c->state != Established){
846 respond(r, "not connected");
849 if(r->ifcall.count == 0){
850 r->ofcall.count = r->ifcall.count;
863 snprint(buf, sizeof buf, "%s!%d\n", localip, localport);
869 remoteread(Req *r, Client *c)
877 snprint(buf, sizeof buf, "%s\n", s);
883 statusread(Req *r, Client *c)
887 s = statestr[c->state];
898 path = r->fid->qid.path;
901 snprint(e, sizeof e, "bug in fsread path=%lux", path);
906 dirread9p(r, rootgen, nil);
915 dirread9p(r, tcpgen, nil);
920 dirread9p(r, clientgen, client[NUM(path)]);
925 ctlread(r, client[NUM(path)]);
929 dataread(r, client[NUM(path)]);
937 remoteread(r, client[NUM(path)]);
941 statusread(r, client[NUM(path)]);
952 path = r->fid->qid.path;
955 snprint(e, sizeof e, "bug in fswrite path=%lux", path);
964 ctlwrite(r, client[NUM(path)]);
968 datawrite(r, client[NUM(path)]);
976 static int need[4] = { 4, 2, 6, 1 };
983 * lib9p already handles the blatantly obvious.
984 * we just have to enforce the permissions we have set.
986 path = r->fid->qid.path;
987 t = &tab[TYPE(path)];
988 n = need[r->ifcall.mode&3];
989 if((n&t->mode) != n){
990 respond(r, "permission denied");
996 cs = emalloc9p(sizeof(Cs));
1002 path = PATH(Qctl, n);
1003 r->fid->qid.path = path;
1004 r->ofcall.qid.path = path;
1006 fprint(2, "open clone => path=%lux\n", path);
1011 client[NUM(path)]->ref++;
1022 for(i=0; i<nclient; i++)
1023 if(findreq(client[i], r->oldreq))
1024 respond(r->oldreq, "interrupted");
1031 int chan, win, pkt, n, l;
1036 case MSG_CHANNEL_WINDOW_ADJUST:
1037 if(unpack(m, "_uu", &chan, &n) < 0)
1039 c = getclient(chan);
1040 if(c != nil && c->state==Established){
1045 case MSG_CHANNEL_DATA:
1046 if(unpack(m, "_us", &chan, &s, &n) < 0)
1048 c = getclient(chan);
1049 if(c != nil && c->state==Established){
1057 case MSG_CHANNEL_EOF:
1058 if(unpack(m, "_u", &chan) < 0)
1060 c = getclient(chan);
1063 m->rp = m->wp = m->buf;
1064 sendmsg(pack(m, "bu", MSG_CHANNEL_CLOSE, c->servernum));
1068 case MSG_CHANNEL_CLOSE:
1069 if(unpack(m, "_u", &chan) < 0)
1071 c = getclient(chan);
1075 case MSG_CHANNEL_OPEN_CONFIRMATION:
1076 if(unpack(m, "_uuuu", &chan, &n, &win, &pkt) < 0)
1078 if(chan == SESSIONCHAN){
1079 sendp(ssherrchan, nil);
1082 c = getclient(chan);
1083 if(c == nil || c->state != Dialing)
1085 if(pkt <= 0 || pkt > MaxPacket)
1090 c->state = Established;
1093 case MSG_CHANNEL_OPEN_FAILURE:
1094 if(unpack(m, "_uus", &chan, &n, &s, &l) < 0)
1096 if(chan == SESSIONCHAN){
1097 sendp(ssherrchan, smprint("%.*s", utfnlen(s, l), s));
1100 c = getclient(chan);
1101 if(c == nil || c->state != Dialing)
1121 threadsetname("fsthread");
1124 a[0].c = fsclunkchan;
1130 a[2].c = sshmsgchan;
1137 path = fid->qid.path;
1147 if(fid->omode != -1 && TYPE(path) >= Qn)
1148 closeclient(client[NUM(path)]);
1149 sendp(fsclunkwaitchan, nil);
1152 switch(r->ifcall.type){
1172 respond(r, "bug in fsthread");
1175 sendp(fsreqwaitchan, 0);
1187 sendp(fsreqchan, r);
1188 recvp(fsreqwaitchan); /* avoids need to deal with spurious flushes */
1192 fsdestroyfid(Fid *fid)
1194 sendp(fsclunkchan, fid);
1195 recvp(fsclunkwaitchan);
1201 threadexitsall("done");
1207 .destroyfid= fsdestroyfid,
1230 if(strncmp(sshargv[0], "./", 2) != 0)
1231 f = smprint("/bin/%s", sshargv[0]);
1234 procexec(nil, f, sshargv);
1235 sysfatal("exec: %r");
1239 ssh(int argc, char *argv[])
1246 sshargv = emalloc9p(sizeof(char *) * (sshargc + 1));
1249 memcpy(sshargv + 2, argv, argc * sizeof(char *));
1253 procrfork(startssh, nil, 8*1024, RFFDG|RFNOTEG|RFNAMEG);
1256 sendmsg(pack(nil, "bsuuu", MSG_CHANNEL_OPEN,
1263 a[0].c = threadwaitchan();
1266 a[1].c = ssherrchan;
1272 sysfatal("ssh failed: %s", w->msg);
1275 sysfatal("ssh failed: %s", e);
1277 chanclose(ssherrchan);
1283 fprint(2, "usage: sshnet [-m mtpt] [ssh options]\n");
1288 threadmain(int argc, char **argv)
1292 fmtinstall('H', encodefmt);
1301 mtpt = EARGF(usage());
1304 service = EARGF(usage());
1314 ssherrchan = chancreate(sizeof(char*), 0);
1315 sshmsgchan = chancreate(sizeof(Msg*), 16);
1316 fsreqchan = chancreate(sizeof(Req*), 0);
1317 fsreqwaitchan = chancreate(sizeof(void*), 0);
1318 fsclunkchan = chancreate(sizeof(Fid*), 0);
1319 fsclunkwaitchan = chancreate(sizeof(void*), 0);
1320 procrfork(fsnetproc, nil, 8*1024, RFNAMEG|RFNOTEG);
1321 procrfork(sshreadproc, nil, 8*1024, RFNAMEG|RFNOTEG);
1325 threadpostmountsrv(&fs, service, mtpt, MREPL);