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) */
86 MSG_CHANNEL_OPEN = 90,
87 MSG_CHANNEL_OPEN_CONFIRMATION,
88 MSG_CHANNEL_OPEN_FAILURE,
89 MSG_CHANNEL_WINDOW_ADJUST,
91 MSG_CHANNEL_EXTENDED_DATA,
99 MaxPacket = (1<<15)-256, /* 32K is maxatomic for pipe */
112 uchar buf[MaxPacket + Overhead];
115 #define PUT4(p, u) (p)[0] = (u)>>24, (p)[1] = (u)>>16, (p)[2] = (u)>>8, (p)[3] = (u)
116 #define GET4(p) (u32int)(p)[3] | (u32int)(p)[2]<<8 | (u32int)(p)[1]<<16 | (u32int)(p)[0]<<24
123 char localip[] = "::";
126 vpack(uchar *p, int n, char *fmt, va_list a)
128 uchar *p0 = p, *e = p+n;
138 if(++p > e) goto err;
141 *va_arg(a, void**) = p;
145 *p++ = va_arg(a, int);
149 s = va_arg(a, void*);
152 if(p+4 > e) goto err;
155 if(u > e-p) goto err;
161 if(p+4 > e) goto err;
171 vunpack(uchar *p, int n, char *fmt, va_list a)
173 uchar *p0 = p, *e = p+n;
182 if(++p > e) goto err;
185 *va_arg(a, void**) = p;
189 *va_arg(a, int*) = *p++;
192 if(p+4 > e) goto err;
194 if(u > e-p) goto err;
195 *va_arg(a, void**) = p;
196 *va_arg(a, int*) = u;
200 s = va_arg(a, void*);
202 if(u > e-p) goto err;
207 if(p+4 > e) goto err;
209 *va_arg(a, int*) = u;
223 m = emalloc9p(sizeof(Msg));
225 m->rp = m->wp = m->buf;
226 m->ep = m->rp + sizeof(m->buf);
231 pack(Msg *m, char *fmt, ...)
239 n = vpack(m->wp, m->ep - m->wp, fmt, a);
241 sysfatal("pack faild");
248 unpack(Msg *m, char *fmt, ...)
254 n = vunpack(m->rp, m->wp - m->rp, fmt, a);
270 if(write(sshfd, m->rp, n) != n)
271 sysfatal("write to ssh failed: %r");
282 for(i=0; i<nclient; i++)
283 if(client[i]->ref==0 && client[i]->state == Closed)
287 client = erealloc9p(client, (nclient+16)*sizeof(client[0]));
289 c = emalloc9p(sizeof(Client));
290 memset(c, 0, sizeof(*c));
292 client[nclient++] = c;
299 if(num < 0 || num >= nclient)
305 adjustwin(Client *c, int len)
308 if(c->recvacc >= MaxPacket*WinPackets/2 || c->recvwin < MaxPacket){
309 sendmsg(pack(nil, "buu", MSG_CHANNEL_WINDOW_ADJUST, c->servernum, c->recvacc));
316 senddata(Client *c, void *data, int len)
318 sendmsg(pack(nil, "bus", MSG_CHANNEL_DATA, c->servernum, (char*)data, len));
323 queuerreq(Client *c, Req *r)
329 c->erq = (Req**)&r->aux;
333 queuermsg(Client *c, Msg *m)
339 c->emq = (Msg**)&m->link;
343 matchrmsgs(Client *c)
349 while((r = c->rq) != nil && (m = c->mq) != nil){
354 if(n >= m->wp - m->rp){
359 memmove(r->ofcall.data, m->rp, n);
370 while((r = c->rq) != nil){
380 queuewreq(Client *c, Req *r)
386 c->ewq = (Req**)&r->aux;
395 while((r = c->wq) != nil && (n = c->sendwin) > 0){
398 if(r->ifcall.count > n){
399 senddata(c, r->ifcall.data, n);
400 r->ifcall.count -= n;
401 memmove(r->ifcall.data, (char*)r->ifcall.data + n, r->ifcall.count);
404 c->wq = (Req*)r->aux;
406 senddata(c, r->ifcall.data, r->ifcall.count);
407 r->ofcall.count = r->ifcall.count;
413 findreq(Client *c, Req *r)
417 for(l=&c->rq; *l; l=(Req**)&(*l)->aux){
425 for(l=&c->wq; *l; l=(Req**)&(*l)->aux){
437 hangupclient(Client *c, char *err)
444 while((r = c->wq) != nil){
453 teardownclient(Client *c)
456 hangupclient(c, "i/o on hungup channel");
457 sendmsg(pack(nil, "bu", MSG_CHANNEL_CLOSE, c->servernum));
461 closeclient(Client *c)
473 sendmsg(pack(nil, "bu", MSG_CHANNEL_CLOSE, c->servernum));
476 while((m = c->mq) != nil){
490 n = read(sshfd, m->rp, m->ep - m->rp);
492 sysfatal("eof on ssh connection");
494 sendp(sshmsgchan, m);
498 typedef struct Tab Tab;
520 fillstat(Dir *d, uvlong path)
524 memset(d, 0, sizeof(*d));
525 d->uid = estrdup9p("ssh");
526 d->gid = estrdup9p("ssh");
528 d->atime = d->mtime = time0;
529 t = &tab[TYPE(path)];
531 d->name = estrdup9p(t->name);
533 d->name = smprint("%ud", NUM(path));
535 sysfatal("out of memory");
537 d->qid.type = t->mode>>24;
544 if(r->ifcall.aname && r->ifcall.aname[0]){
545 respond(r, "invalid attach specifier");
548 r->fid->qid.path = PATH(Qroot, 0);
549 r->fid->qid.type = QTDIR;
550 r->fid->qid.vers = 0;
551 r->ofcall.qid = r->fid->qid;
558 fillstat(&r->d, r->fid->qid.path);
563 rootgen(int i, Dir *d, void*)
574 tcpgen(int i, Dir *d, void*)
583 fillstat(d, PATH(Qn, i));
590 clientgen(int i, Dir *d, void *aux)
597 fillstat(d, PATH(i, c->num));
604 fswalk1(Fid *fid, char *name, Qid *qid)
610 path = fid->qid.path;
611 if(!(fid->qid.type&QTDIR))
612 return "walk in non-directory";
614 if(strcmp(name, "..") == 0){
617 qid->path = PATH(Qtcp, NUM(path));
618 qid->type = tab[Qtcp].mode>>24;
621 qid->path = PATH(Qroot, 0);
622 qid->type = tab[Qroot].mode>>24;
627 return "bug in fswalk1";
632 for(; i<nelem(tab); i++){
635 snprint(buf, sizeof buf, "%d", n);
636 if(n < nclient && strcmp(buf, name) == 0){
637 qid->path = PATH(i, n);
638 qid->type = tab[i].mode>>24;
643 if(strcmp(name, tab[i].name) == 0){
644 qid->path = PATH(i, NUM(path));
645 qid->type = tab[i].mode>>24;
648 if(tab[i].mode&DMDIR)
651 return "directory entry not found";
654 typedef struct Cs Cs;
671 n = strtol(p, &s, 0);
676 db = ndbopen("/lib/ndb/common");
681 port = ndbgetvalue(db, nil, "tcp", p, "port", nil);
697 respond(r, "cs read without write");
700 if(r->ifcall.offset==0){
708 readstr(r, cs->resp);
716 char err[ERRMAX], *f[4], *s, *ns;
720 s = emalloc9p(r->ifcall.count+1);
721 memmove(s, r->ifcall.data, r->ifcall.count);
722 s[r->ifcall.count] = '\0';
724 nf = getfields(s, f, nelem(f), 0, "!");
727 respond(r, "can't translate");
730 if(strcmp(f[0], "tcp") != 0 && strcmp(f[0], "net") != 0){
732 respond(r, "unknown protocol");
735 port = ndbfindport(f[2]);
738 respond(r, "no translation found");
742 ns = smprint("%s/tcp/clone %s!%d", mtpt, f[1], port);
745 rerrstr(err, sizeof err);
753 r->ofcall.count = r->ifcall.count;
758 ctlread(Req *r, Client *c)
762 sprint(buf, "%d", c->num);
768 ctlwrite(Req *r, Client *c)
773 s = emalloc9p(r->ifcall.count+1);
774 r->ofcall.count = r->ifcall.count;
775 memmove(s, r->ifcall.data, r->ifcall.count);
776 s[r->ifcall.count] = '\0';
778 nf = tokenize(s, f, 3);
785 if(strcmp(f[0], "hangup") == 0){
786 if(c->state != Established)
792 }else if(strcmp(f[0], "connect") == 0){
793 if(c->state != Closed)
798 c->connect = estrdup9p(f[1]);
799 nf = getfields(f[1], f, nelem(f), 0, "!");
802 c->recvwin = WinPackets*MaxPacket;
807 sendmsg(pack(nil, "bsuuususu", MSG_CHANNEL_OPEN,
809 c->num, c->recvwin, MaxPacket,
810 f[0], strlen(f[0]), ndbfindport(f[1]),
811 localip, strlen(localip), localport));
814 respond(r, "bad or inappropriate tcp control message");
820 dataread(Req *r, Client *c)
822 if(c->state < Established){
823 respond(r, "not connected");
831 datawrite(Req *r, Client *c)
833 if(c->state != Established){
834 respond(r, "not connected");
837 if(r->ifcall.count == 0){
838 r->ofcall.count = r->ifcall.count;
851 snprint(buf, sizeof buf, "%s!%d\n", localip, localport);
857 remoteread(Req *r, Client *c)
865 snprint(buf, sizeof buf, "%s\n", s);
871 statusread(Req *r, Client *c)
875 s = statestr[c->state];
886 path = r->fid->qid.path;
889 snprint(e, sizeof e, "bug in fsread path=%lux", path);
894 dirread9p(r, rootgen, nil);
903 dirread9p(r, tcpgen, nil);
908 dirread9p(r, clientgen, client[NUM(path)]);
913 ctlread(r, client[NUM(path)]);
917 dataread(r, client[NUM(path)]);
925 remoteread(r, client[NUM(path)]);
929 statusread(r, client[NUM(path)]);
940 path = r->fid->qid.path;
943 snprint(e, sizeof e, "bug in fswrite path=%lux", path);
952 ctlwrite(r, client[NUM(path)]);
956 datawrite(r, client[NUM(path)]);
964 static int need[4] = { 4, 2, 6, 1 };
971 * lib9p already handles the blatantly obvious.
972 * we just have to enforce the permissions we have set.
974 path = r->fid->qid.path;
975 t = &tab[TYPE(path)];
976 n = need[r->ifcall.mode&3];
977 if((n&t->mode) != n){
978 respond(r, "permission denied");
984 cs = emalloc9p(sizeof(Cs));
990 path = PATH(Qctl, n);
991 r->fid->qid.path = path;
992 r->ofcall.qid.path = path;
994 fprint(2, "open clone => path=%lux\n", path);
999 client[NUM(path)]->ref++;
1010 for(i=0; i<nclient; i++)
1011 if(findreq(client[i], r->oldreq))
1012 respond(r->oldreq, "interrupted");
1019 int chan, win, pkt, n;
1024 case MSG_CHANNEL_WINDOW_ADJUST:
1025 if(unpack(m, "_uu", &chan, &n) < 0)
1027 c = getclient(chan);
1028 if(c != nil && c->state == Established){
1033 case MSG_CHANNEL_DATA:
1034 if(unpack(m, "_us", &chan, &s, &n) < 0)
1036 c = getclient(chan);
1037 if(c != nil && c->state == Established){
1047 case MSG_CHANNEL_EOF:
1048 if(unpack(m, "_u", &chan) < 0)
1050 c = getclient(chan);
1051 if(c != nil && c->state == Established){
1057 case MSG_CHANNEL_CLOSE:
1058 if(unpack(m, "_u", &chan) < 0)
1060 c = getclient(chan);
1065 c->state = Finished;
1066 hangupclient(c, "connection closed");
1073 case MSG_CHANNEL_OPEN_CONFIRMATION:
1074 if(unpack(m, "_uuuu", &chan, &n, &win, &pkt) < 0)
1076 if(chan == SESSIONCHAN){
1077 sendp(ssherrchan, nil);
1080 c = getclient(chan);
1081 if(c == nil || c->state != Dialing)
1083 if(pkt <= 0 || pkt > MaxPacket)
1089 c->state = Established;
1091 respond(c->wq, nil);
1095 case MSG_CHANNEL_OPEN_FAILURE:
1096 if(unpack(m, "_u____s", &chan, &s, &n) < 0)
1098 s = smprint("%.*s", utfnlen(s, n), s);
1099 if(chan == SESSIONCHAN){
1100 sendp(ssherrchan, s);
1103 c = getclient(chan);
1104 if(c == nil || c->state != Dialing){
1125 threadsetname("fsthread");
1128 a[0].c = fsclunkchan;
1134 a[2].c = sshmsgchan;
1141 path = fid->qid.path;
1151 if(fid->omode != -1 && TYPE(path) >= Qn)
1152 closeclient(client[NUM(path)]);
1153 sendp(fsclunkwaitchan, nil);
1156 switch(r->ifcall.type){
1176 respond(r, "bug in fsthread");
1179 sendp(fsreqwaitchan, 0);
1191 sendp(fsreqchan, r);
1192 recvp(fsreqwaitchan); /* avoids need to deal with spurious flushes */
1196 fsdestroyfid(Fid *fid)
1198 sendp(fsclunkchan, fid);
1199 recvp(fsclunkwaitchan);
1205 threadexitsall("done");
1211 .destroyfid= fsdestroyfid,
1234 if(strncmp(sshargv[0], "./", 2) != 0)
1235 f = smprint("/bin/%s", sshargv[0]);
1238 procexec(nil, f, sshargv);
1239 sysfatal("exec: %r");
1243 ssh(int argc, char *argv[])
1250 sshargv = emalloc9p(sizeof(char *) * (sshargc + 1));
1253 memcpy(sshargv + 2, argv, argc * sizeof(char *));
1257 procrfork(startssh, nil, 8*1024, RFFDG|RFNOTEG|RFNAMEG);
1260 sendmsg(pack(nil, "bsuuu", MSG_CHANNEL_OPEN,
1267 a[0].c = threadwaitchan();
1270 a[1].c = ssherrchan;
1276 sysfatal("ssh failed: %s", w->msg);
1279 sysfatal("ssh failed: %s", e);
1281 chanclose(ssherrchan);
1287 fprint(2, "usage: sshnet [-m mtpt] [ssh options]\n");
1292 threadmain(int argc, char **argv)
1296 fmtinstall('H', encodefmt);
1305 mtpt = EARGF(usage());
1308 service = EARGF(usage());
1318 ssherrchan = chancreate(sizeof(char*), 0);
1319 sshmsgchan = chancreate(sizeof(Msg*), 16);
1320 fsreqchan = chancreate(sizeof(Req*), 0);
1321 fsreqwaitchan = chancreate(sizeof(void*), 0);
1322 fsclunkchan = chancreate(sizeof(Fid*), 0);
1323 fsclunkwaitchan = chancreate(sizeof(void*), 0);
1324 procrfork(fsnetproc, nil, 8*1024, RFNAMEG|RFNOTEG);
1325 procrfork(sshreadproc, nil, 8*1024, RFNAMEG|RFNOTEG);
1329 threadpostmountsrv(&fs, service, mtpt, MREPL);