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 *sshmsgchan; /* chan(Msg*) */
36 Channel *fsreqchan; /* chan(Req*) */
37 Channel *fsreqwaitchan; /* chan(nil) */
38 Channel *fsclunkchan; /* chan(Fid*) */
39 Channel *fsclunkwaitchan; /* chan(nil) */
81 MSG_CHANNEL_OPEN = 90,
82 MSG_CHANNEL_OPEN_CONFIRMATION,
83 MSG_CHANNEL_OPEN_FAILURE,
84 MSG_CHANNEL_WINDOW_ADJUST,
86 MSG_CHANNEL_EXTENDED_DATA,
104 uchar buf[MaxPacket];
107 #define PUT4(p, u) (p)[0] = (u)>>24, (p)[1] = (u)>>16, (p)[2] = (u)>>8, (p)[3] = (u)
108 #define GET4(p) (u32int)(p)[3] | (u32int)(p)[2]<<8 | (u32int)(p)[1]<<16 | (u32int)(p)[0]<<24
115 char localip[] = "::";
118 vpack(uchar *p, int n, char *fmt, va_list a)
120 uchar *p0 = p, *e = p+n;
130 if(++p > e) goto err;
133 *va_arg(a, void**) = p;
137 *p++ = va_arg(a, int);
141 s = va_arg(a, void*);
144 if(p+4 > e) goto err;
147 if(u > e-p) goto err;
153 if(p+4 > e) goto err;
163 vunpack(uchar *p, int n, char *fmt, va_list a)
165 uchar *p0 = p, *e = p+n;
174 if(++p > e) goto err;
177 *va_arg(a, void**) = p;
181 *va_arg(a, int*) = *p++;
184 if(p+4 > e) goto err;
186 if(u > e-p) goto err;
187 *va_arg(a, void**) = p;
188 *va_arg(a, int*) = u;
192 s = va_arg(a, void*);
194 if(u > e-p) goto err;
199 if(p+4 > e) goto err;
201 *va_arg(a, int*) = u;
215 m = emalloc9p(sizeof(Msg));
217 m->rp = m->wp = m->buf;
218 m->ep = m->rp + sizeof(m->buf);
223 pack(Msg *m, char *fmt, ...)
231 n = vpack(m->wp, m->ep - m->wp, fmt, a);
233 sysfatal("pack faild");
240 unpack(Msg *m, char *fmt, ...)
246 n = vunpack(m->rp, m->wp - m->rp, fmt, a);
262 if(write(sshfd, m->rp, n) != n)
263 sysfatal("write to ssh failed: %r");
274 for(i=0; i<nclient; i++)
275 if(client[i]->ref==0 && client[i]->state == Closed)
279 client = erealloc9p(client, (nclient+16)*sizeof(client[0]));
281 c = emalloc9p(sizeof(Client));
282 memset(c, 0, sizeof(*c));
284 client[nclient++] = c;
291 if(num < 0 || num >= nclient)
297 adjustwin(Client *c, int len)
300 if(c->recvacc >= MaxPacket*WinPackets/2 || c->recvwin < MaxPacket){
301 sendmsg(pack(nil, "buu", MSG_CHANNEL_WINDOW_ADJUST, c->servernum, c->recvacc));
308 senddata(Client *c, void *data, int len)
310 sendmsg(pack(nil, "bus", MSG_CHANNEL_DATA, c->servernum, (char*)data, len));
315 queuerreq(Client *c, Req *r)
321 c->erq = (Req**)&r->aux;
325 queuermsg(Client *c, Msg *m)
331 c->emq = (Msg**)&m->link;
335 matchrmsgs(Client *c)
341 while(c->rq != nil && c->mq != nil){
348 if(n >= m->wp - m->rp){
353 memmove(r->ofcall.data, m->rp, n);
365 queuewreq(Client *c, Req *r)
371 c->ewq = (Req**)&r->aux;
380 while((r = c->wq) != nil && (n = c->sendwin) > 0){
383 if(r->ifcall.count > n){
384 senddata(c, r->ifcall.data, n);
385 r->ifcall.count -= n;
386 memmove(r->ifcall.data, (char*)r->ifcall.data + n, r->ifcall.count);
389 c->wq = (Req*)r->aux;
391 senddata(c, r->ifcall.data, r->ifcall.count);
392 r->ofcall.count = r->ifcall.count;
398 findreq(Client *c, Req *r)
402 for(l=&c->rq; *l; l=(Req**)&(*l)->aux){
410 for(l=&c->wq; *l; l=(Req**)&(*l)->aux){
422 dialedclient(Client *c)
428 sysfatal("more than one outstanding dial request (BUG)");
429 if(c->state == Established)
432 respond(r, "connect failed");
438 teardownclient(Client *c)
441 sendmsg(pack(nil, "bu", MSG_CHANNEL_EOF, c->servernum));
445 hangupclient(Client *c)
451 for(m=c->mq; m; m=mnext){
456 for(r=c->rq; r; r=next){
458 respond(r, "hangup on network connection");
461 for(r=c->wq; r; r=next){
463 respond(r, "hangup on network connection");
469 closeclient(Client *c)
476 if(c->rq != nil || c->wq != nil)
477 sysfatal("ref count reached zero with requests pending (BUG)");
479 for(m=c->mq; m; m=next){
485 if(c->state != Closed)
498 n = read(sshfd, m->rp, m->ep - m->rp);
500 sysfatal("eof on ssh connection");
502 sendp(sshmsgchan, m);
506 typedef struct Tab Tab;
528 fillstat(Dir *d, uvlong path)
532 memset(d, 0, sizeof(*d));
533 d->uid = estrdup9p("ssh");
534 d->gid = estrdup9p("ssh");
536 d->atime = d->mtime = time0;
537 t = &tab[TYPE(path)];
539 d->name = estrdup9p(t->name);
541 d->name = smprint("%ud", NUM(path));
543 sysfatal("out of memory");
545 d->qid.type = t->mode>>24;
552 if(r->ifcall.aname && r->ifcall.aname[0]){
553 respond(r, "invalid attach specifier");
556 r->fid->qid.path = PATH(Qroot, 0);
557 r->fid->qid.type = QTDIR;
558 r->fid->qid.vers = 0;
559 r->ofcall.qid = r->fid->qid;
566 fillstat(&r->d, r->fid->qid.path);
571 rootgen(int i, Dir *d, void*)
582 tcpgen(int i, Dir *d, void*)
591 fillstat(d, PATH(Qn, i));
598 clientgen(int i, Dir *d, void *aux)
605 fillstat(d, PATH(i, c->num));
612 fswalk1(Fid *fid, char *name, Qid *qid)
618 path = fid->qid.path;
619 if(!(fid->qid.type&QTDIR))
620 return "walk in non-directory";
622 if(strcmp(name, "..") == 0){
625 qid->path = PATH(Qtcp, NUM(path));
626 qid->type = tab[Qtcp].mode>>24;
629 qid->path = PATH(Qroot, 0);
630 qid->type = tab[Qroot].mode>>24;
635 return "bug in fswalk1";
640 for(; i<nelem(tab); i++){
643 snprint(buf, sizeof buf, "%d", n);
644 if(n < nclient && strcmp(buf, name) == 0){
645 qid->path = PATH(i, n);
646 qid->type = tab[i].mode>>24;
651 if(strcmp(name, tab[i].name) == 0){
652 qid->path = PATH(i, NUM(path));
653 qid->type = tab[i].mode>>24;
656 if(tab[i].mode&DMDIR)
659 return "directory entry not found";
662 typedef struct Cs Cs;
679 n = strtol(p, &s, 0);
684 db = ndbopen("/lib/ndb/common");
689 port = ndbgetvalue(db, nil, "tcp", p, "port", nil);
705 respond(r, "cs read without write");
708 if(r->ifcall.offset==0){
716 readstr(r, cs->resp);
724 char err[ERRMAX], *f[4], *s, *ns;
728 s = emalloc9p(r->ifcall.count+1);
729 memmove(s, r->ifcall.data, r->ifcall.count);
730 s[r->ifcall.count] = '\0';
732 nf = getfields(s, f, nelem(f), 0, "!");
735 respond(r, "can't translate");
738 if(strcmp(f[0], "tcp") != 0 && strcmp(f[0], "net") != 0){
740 respond(r, "unknown protocol");
743 port = ndbfindport(f[2]);
746 respond(r, "no translation found");
750 ns = smprint("%s/tcp/clone %s!%d", mtpt, f[1], port);
753 rerrstr(err, sizeof err);
761 r->ofcall.count = r->ifcall.count;
766 ctlread(Req *r, Client *c)
770 sprint(buf, "%d", c->num);
776 ctlwrite(Req *r, Client *c)
781 s = emalloc9p(r->ifcall.count+1);
782 memmove(s, r->ifcall.data, r->ifcall.count);
783 s[r->ifcall.count] = '\0';
785 nf = tokenize(s, f, 3);
788 r->ofcall.count = r->ifcall.count;
793 if(strcmp(f[0], "hangup") == 0){
794 if(c->state != Established)
799 r->ofcall.count = r->ifcall.count;
801 }else if(strcmp(f[0], "connect") == 0){
802 if(c->state != Closed)
806 c->connect = estrdup9p(f[1]);
807 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;
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 c = getclient(chan);
1079 if(c == nil || c->state != Dialing)
1081 if(pkt <= 0 || pkt > MaxPacket)
1086 c->state = Established;
1089 case MSG_CHANNEL_OPEN_FAILURE:
1090 if(unpack(m, "_uu", &chan, &n) < 0)
1092 c = getclient(chan);
1093 if(c == nil || c->state != Dialing)
1113 threadsetname("fsthread");
1116 a[0].c = fsclunkchan;
1122 a[2].c = sshmsgchan;
1129 path = fid->qid.path;
1139 if(fid->omode != -1 && TYPE(path) >= Qn)
1140 closeclient(client[NUM(path)]);
1141 sendp(fsclunkwaitchan, nil);
1144 switch(r->ifcall.type){
1164 respond(r, "bug in fsthread");
1167 sendp(fsreqwaitchan, 0);
1179 sendp(fsreqchan, r);
1180 recvp(fsreqwaitchan); /* avoids need to deal with spurious flushes */
1184 fsdestroyfid(Fid *fid)
1186 sendp(fsclunkchan, fid);
1187 recvp(fsclunkwaitchan);
1193 threadexitsall("done");
1199 .destroyfid= fsdestroyfid,
1222 if(strncmp(sshargv[0], "./", 2) != 0)
1223 f = smprint("/bin/%s", sshargv[0]);
1226 procexec(nil, f, sshargv);
1227 sysfatal("exec: %r");
1233 fprint(2, "usage: sshnet [-m mtpt] [ssh options]\n");
1238 threadmain(int argc, char **argv)
1242 fmtinstall('H', encodefmt);
1251 mtpt = EARGF(usage());
1254 service = EARGF(usage());
1264 sshargv = emalloc9p(sizeof(char *) * (sshargc + 1));
1267 memcpy(sshargv + 2, argv, argc * sizeof(char *));
1271 procrfork(startssh, nil, mainstacksize, RFFDG|RFNOTEG|RFNAMEG);
1275 sshmsgchan = chancreate(sizeof(Msg*), 16);
1276 fsreqchan = chancreate(sizeof(Req*), 0);
1277 fsreqwaitchan = chancreate(sizeof(void*), 0);
1278 fsclunkchan = chancreate(sizeof(Fid*), 0);
1279 fsclunkwaitchan = chancreate(sizeof(void*), 0);
1281 procrfork(sshreadproc, nil, mainstacksize, RFNAMEG|RFNOTEG);
1282 procrfork(fsnetproc, nil, mainstacksize, RFNAMEG|RFNOTEG);
1284 threadpostmountsrv(&fs, service, mtpt, MREPL);