11 #define dprint(...) if(debug) fprint(2, __VA_ARGS__)
12 #pragma varargck type "Σ" int
33 SSH_FXP_FSETSTAT = 10,
39 SSH_FXP_REALPATH = 16,
42 SSH_FXP_READLINK = 19,
49 SSH_FXP_EXTENDED = 200,
50 SSH_FXP_EXTENDED_REPLY = 201,
52 SSH_FXF_READ = 0x00000001,
53 SSH_FXF_WRITE = 0x00000002,
54 SSH_FXF_APPEND = 0x00000004,
55 SSH_FXF_CREAT = 0x00000008,
56 SSH_FXF_TRUNC = 0x00000010,
57 SSH_FXF_EXCL = 0x00000020,
58 SSH_FILEXFER_ATTR_SIZE = 0x00000001,
59 SSH_FILEXFER_ATTR_UIDGID = 0x00000002,
60 SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004,
61 SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008,
62 SSH_FILEXFER_ATTR_EXTENDED = 0x80000000,
66 SSH_FX_NO_SUCH_FILE = 2,
67 SSH_FX_PERMISSION_DENIED = 3,
69 SSH_FX_BAD_MESSAGE = 5,
70 SSH_FX_NO_CONNECTION = 6,
71 SSH_FX_CONNECTION_LOST = 7,
72 SSH_FX_OP_UNSUPPORTED = 8,
76 [SSH_FX_OK] "success",
77 [SSH_FX_EOF] "end of file",
78 [SSH_FX_NO_SUCH_FILE] "file does not exist",
79 [SSH_FX_PERMISSION_DENIED] "permission denied",
80 [SSH_FX_FAILURE] "failure",
81 [SSH_FX_BAD_MESSAGE] "bad message",
82 [SSH_FX_NO_CONNECTION] "no connection",
83 [SSH_FX_CONNECTION_LOST] "connection lost",
84 [SSH_FX_OP_UNSUPPORTED] "unsupported operation",
87 typedef struct SFid SFid;
88 typedef struct SReq SReq;
89 typedef struct IDEnt IDEnt;
115 IDEnt *uidtab[HASH], *gidtab[HASH];
118 SReq *sreqrd[MAXREQID];
120 Rendez sreqidrend = {.l = &sreqidlock};
122 SReq *sreqwr, **sreqlast = &sreqwr;
124 Rendez writerend = {.l = &sreqwrlock};
126 #define PUT4(p, u) (p)[0] = (u)>>24, (p)[1] = (u)>>16, (p)[2] = (u)>>8, (p)[3] = (u)
127 #define GET4(p) ((u32int)(p)[3] | (u32int)(p)[2]<<8 | (u32int)(p)[1]<<16 | (u32int)(p)[0]<<24)
134 n = va_arg(f->args, int);
136 case SSH_FXP_INIT: fmtstrcpy(f, "SSH_FXP_INIT"); break;
137 case SSH_FXP_VERSION: fmtstrcpy(f, "SSH_FXP_VERSION"); break;
138 case SSH_FXP_OPEN: fmtstrcpy(f, "SSH_FXP_OPEN"); break;
139 case SSH_FXP_CLOSE: fmtstrcpy(f, "SSH_FXP_CLOSE"); break;
140 case SSH_FXP_READ: fmtstrcpy(f, "SSH_FXP_READ"); break;
141 case SSH_FXP_WRITE: fmtstrcpy(f, "SSH_FXP_WRITE"); break;
142 case SSH_FXP_LSTAT: fmtstrcpy(f, "SSH_FXP_LSTAT"); break;
143 case SSH_FXP_FSTAT: fmtstrcpy(f, "SSH_FXP_FSTAT"); break;
144 case SSH_FXP_SETSTAT: fmtstrcpy(f, "SSH_FXP_SETSTAT"); break;
145 case SSH_FXP_FSETSTAT: fmtstrcpy(f, "SSH_FXP_FSETSTAT"); break;
146 case SSH_FXP_OPENDIR: fmtstrcpy(f, "SSH_FXP_OPENDIR"); break;
147 case SSH_FXP_READDIR: fmtstrcpy(f, "SSH_FXP_READDIR"); break;
148 case SSH_FXP_REMOVE: fmtstrcpy(f, "SSH_FXP_REMOVE"); break;
149 case SSH_FXP_MKDIR: fmtstrcpy(f, "SSH_FXP_MKDIR"); break;
150 case SSH_FXP_RMDIR: fmtstrcpy(f, "SSH_FXP_RMDIR"); break;
151 case SSH_FXP_REALPATH: fmtstrcpy(f, "SSH_FXP_REALPATH"); break;
152 case SSH_FXP_STAT: fmtstrcpy(f, "SSH_FXP_STAT"); break;
153 case SSH_FXP_RENAME: fmtstrcpy(f, "SSH_FXP_RENAME"); break;
154 case SSH_FXP_READLINK: fmtstrcpy(f, "SSH_FXP_READLINK"); break;
155 case SSH_FXP_SYMLINK: fmtstrcpy(f, "SSH_FXP_SYMLINK"); break;
156 case SSH_FXP_STATUS: fmtstrcpy(f, "SSH_FXP_STATUS"); break;
157 case SSH_FXP_HANDLE: fmtstrcpy(f, "SSH_FXP_HANDLE"); break;
158 case SSH_FXP_DATA: fmtstrcpy(f, "SSH_FXP_DATA"); break;
159 case SSH_FXP_NAME: fmtstrcpy(f, "SSH_FXP_NAME"); break;
160 case SSH_FXP_ATTRS: fmtstrcpy(f, "SSH_FXP_ATTRS"); break;
161 case SSH_FXP_EXTENDED: fmtstrcpy(f, "SSH_FXP_EXTENDED"); break;
162 case SSH_FXP_EXTENDED_REPLY: fmtstrcpy(f, "SSH_FXP_EXTENDED_REPLY");
163 default: fmtprint(f, "%d", n);
169 idlookup(IDEnt **tab, int id)
173 for(p = tab[(ulong)id % HASH]; p != nil; p = p->next)
175 return estrdup9p(p->name);
176 return smprint("%d", id);
180 namelookup(IDEnt **tab, char *name)
186 for(i = 0; i < HASH; i++)
187 for(p = tab[i]; p != nil; p = p->next)
188 if(strcmp(p->name, name) == 0)
190 i = strtol(name, &q, 10);
191 if(*q == 0) return i;
192 werrstr("unknown %s '%s'", tab == uidtab ? "user" : "group", name);
197 vpack(uchar *p, int n, char *fmt, va_list a)
199 uchar *p0 = p, *e = p+n;
210 if(++p > e) goto err;
213 *va_arg(a, void**) = p;
217 *p++ = va_arg(a, int);
221 s = va_arg(a, void*);
224 if(p+4 > e) goto err;
227 if(u > e-p) goto err;
233 if(p+4 > e) goto err;
237 v = va_arg(a, vlong);
238 if(p+8 > e) goto err;
239 u = v>>32; PUT4(p, u), p += 4;
240 u = v; PUT4(p, u), p += 4;
249 vunpack(uchar *p, int n, char *fmt, va_list a)
251 uchar *p0 = p, *e = p+n;
261 if(++p > e) goto err;
264 *va_arg(a, void**) = p;
268 *va_arg(a, int*) = *p++;
271 if(p+4 > e) goto err;
273 if(u > e-p) goto err;
274 *va_arg(a, void**) = p;
275 *va_arg(a, int*) = u;
279 s = va_arg(a, void*);
281 if(u > e-p) goto err;
286 if(p+4 > e) goto err;
288 *va_arg(a, int*) = u;
292 if(p+8 > e) goto err;
293 v = (u64int)GET4(p) << 32;
294 v |= (u32int)GET4(p+4);
295 *va_arg(a, vlong*) = v;
305 pack(uchar *p, int n, char *fmt, ...)
309 n = vpack(p, n, fmt, a);
314 unpack(uchar *p, int n, char *fmt, ...)
318 n = vunpack(p, n, fmt, a);
324 sendpkt(char *fmt, ...)
326 static uchar buf[MAXPACK];
331 n = vpack(buf+4, sizeof(buf)-4, fmt, a);
334 sysfatal("sendpkt: message too big");
340 dprint("SFTP --> %Σ\n", (int)buf[4]);
341 if(write(wrfd, buf, n) != n)
342 sysfatal("write: %r");
345 static uchar rxpkt[MAXPACK];
351 static uchar rxbuf[MAXPACK];
355 while(rxfill < 4 || rxfill < (rxlen = GET4(rxbuf) + 4) && rxlen <= MAXPACK){
356 rc = read(rdfd, rxbuf + rxfill, MAXPACK - rxfill);
357 if(rc < 0) sysfatal("read: %r");
358 if(rc == 0) sysfatal("read: eof");
361 if(rxlen > MAXPACK) sysfatal("received garbage");
362 memmove(rxpkt, rxbuf + 4, rxlen - 4);
363 memmove(rxbuf, rxbuf + rxlen, rxfill - rxlen);
366 dprint("SFTP <-- %Σ\n", (int)rxpkt[0]);
384 for(i = 0; i < s->ndirent; i++)
385 freedir1(&s->dirent[i]);
409 sreqrd[s->reqid] = nil;
410 rwakeup(&sreqidrend);
411 qunlock(&sreqidlock);
413 putsfid(s->closefid);
424 qunlock(&sreqwrlock);
433 s = emalloc9p(sizeof(SReq));
440 pathcat(char *p, char *c)
442 return cleanname(smprint("%s/%s", p, c));
448 return pathcat(p, "..");
457 if(q == nil) return estrdup9p(p);
458 return estrdup9p(q+1);
466 sha1((uchar *) c, strlen(c), dig, nil);
467 return dig[0] | dig[1] << 8 | dig[2] << 16 | dig[3] << 24 | (uvlong)dig[4] << 32 | (uvlong)dig[5] << 40 | (uvlong)dig[6] << 48 | (uvlong)dig[7] << 56;
471 walkprocess(Req *r, char *e)
479 if(r->ofcall.nwqid == 0){
484 r->aux = parentdir(p);
488 assert(r->ofcall.nwqid > 0);
493 sf->qid = r->ofcall.wqid[r->ofcall.nwqid - 1];
500 attrib2dir(uchar *p0, uchar *ep, Dir *d)
503 int i, rc, extn, extvn;
504 u32int flags, uid, gid, perm, next;
508 if(p + 4 > ep) return -1;
509 flags = GET4(p), p += 4;
510 if((flags & SSH_FILEXFER_ATTR_SIZE) != 0){
511 rc = unpack(p, ep - p, "v", &d->length); if(rc < 0) return -1; p += rc;
513 if((flags & SSH_FILEXFER_ATTR_UIDGID) != 0){
514 rc = unpack(p, ep - p, "uu", &uid, &gid); if(rc < 0) return -1; p += rc;
515 d->uid = idlookup(uidtab, uid);
516 d->gid = idlookup(gidtab, gid);
518 d->uid = estrdup9p("sshfs");
519 d->gid = estrdup9p("sshfs");
521 d->muid = estrdup9p(d->uid);
522 if((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0){
523 rc = unpack(p, ep - p, "u", &perm); if(rc < 0) return -1; p += rc;
524 d->mode = perm & 0777;
525 if((perm & 0170000) == 0040000) d->mode |= DMDIR;
527 d->qid.type = d->mode >> 24;
528 if((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0){
529 rc = unpack(p, ep - p, "uu", &d->atime, &d->mtime); if(rc < 0) return -1; p += rc;
530 d->qid.vers = d->mtime;
532 if((flags & SSH_FILEXFER_ATTR_EXTENDED) != 0){
533 rc = unpack(p, ep - p, "u", &next); if(rc < 0) return -1; p += rc;
534 for(i = 0; i < next; i++){
535 rc = unpack(p, ep - p, "ss", &exts, &extn, &extvs, &extvn); if(rc < 0) return -1; p += rc;
536 exts[extn] = extvs[extvn] = 0;
543 dir2attrib(Dir *d, uchar **rp)
550 werrstr("phase error");
551 r = emalloc9p(MAXATTRIB);
555 if(d->length != (uvlong)-1){
556 fl |= SSH_FILEXFER_ATTR_SIZE;
557 rc = pack(p, e - p, "v", d->length); if(rc < 0) return -1; p += rc;
559 if(d->uid != nil && *d->uid != 0 || d->gid != nil && *d->gid != 0){
560 /* FIXME: sending -1 for "don't change" works with openssh, but violates the spec */
561 if(d->uid != nil && *d->uid != 0){
562 uid = namelookup(uidtab, d->uid);
567 if(d->gid != nil && *d->gid != 0){
568 gid = namelookup(gidtab, d->gid);
573 fl |= SSH_FILEXFER_ATTR_UIDGID;
574 rc = pack(p, e - p, "uu", uid, gid); if(rc < 0) return -1; p += rc;
576 if(d->mode != (ulong)-1){
577 fl |= SSH_FILEXFER_ATTR_PERMISSIONS;
578 rc = pack(p, e - p, "u", d->mode); if(rc < 0) return -1; p += rc;
580 if(d->atime != (ulong)-1 || d->mtime != (ulong)-1){
581 /* FIXME: see above */
582 fl |= SSH_FILEXFER_ATTR_ACMODTIME;
583 rc = pack(p, e - p, "uu", d->atime, d->mtime); if(rc < 0) return -1; p += rc;
591 attrfixupqid(Qid *qid)
596 if(unpack(rxpkt, rxlen, "_____u", &flags) < 0) return -1;
598 if(flags & SSH_FILEXFER_ATTR_SIZE) p += 8;
599 if(flags & SSH_FILEXFER_ATTR_UIDGID) p += 8;
600 if(flags & SSH_FILEXFER_ATTR_PERMISSIONS){
601 if(p + 4 > rxpkt + rxlen) return -1;
602 if((GET4(p) & 0170000) != 0040000) qid->type = 0;
603 else qid->type = QTDIR;
606 if(flags & SSH_FILEXFER_ATTR_ACMODTIME){
607 if(p + 8 > rxpkt + rxlen) return -1;
609 qid->vers = GET4(p); /* mtime for qid.vers */
625 if(unpack(rxpkt, rxlen, "_____u", &c) < 0) return -1;
628 sf->dirent = emalloc9p(c * sizeof(Dir));
632 for(i = 0; i < c; i++){
633 memset(d, 0, sizeof(Dir));
634 rc = unpack(p, ep - p, "ss", &fn, &fns, &ln, &lns); if(rc < 0) goto err; p += rc;
635 rc = attrib2dir(p, ep, d); if(rc < 0) goto err; p += rc;
636 if(fn[0] == '.' && (fns == 1 || fns == 2 && fn[1] == '.')){
640 d->name = emalloc9p(fns + 1);
641 memcpy(d->name, fn, fns);
643 s = pathcat(sf->fn, d->name);
644 d->qid.path = qidcalc(s);
674 p = (uchar*)r->ofcall.data + r->ofcall.count;
675 ep = (uchar*)r->ofcall.data + r->ifcall.count;
680 rv = convD2M(&sf->dirent[i], p, ep-p);
690 r->ofcall.count = p - (uchar*)r->ofcall.data;
702 if((r->fid->qid.type & QTDIR) == 0){
707 if(r->ifcall.offset == 0){
710 if(sf->dirreads > 0){
727 sf = emalloc9p(sizeof(SFid));
728 if(r->ifcall.aname != nil)
729 switch(*r->ifcall.aname){
731 switch(r->ifcall.aname[1]){
732 case 0: sf->fn = estrdup9p("."); break;
733 case '/': sf->fn = estrdup9p(r->ifcall.aname + 2); break;
736 respond(r, "invalid attach name");
741 sf->fn = estrdup9p(r->ifcall.aname);
744 sf->fn = estrdup9p(root);
747 sf->fn = pathcat(root, r->ifcall.aname);
750 sf->fn = estrdup9p(root);
755 sf->qid = (Qid){qidcalc(sf->fn), 0, QTDIR};
756 r->ofcall.qid = sf->qid;
757 r->fid->qid = sf->qid;
771 threadsetname("send");
779 if(sreqwr == nil) sreqlast = &sreqwr;
780 qunlock(&sreqwrlock);
784 for(i = 0; i < MAXREQID; i++)
785 if(sreqrd[i] == nil){
794 qunlock(&sreqidlock);
796 if(r->closefid != nil){
797 sendpkt("bus", SSH_FXP_CLOSE, r->reqid, r->closefid->hand, r->closefid->handn);
801 sysfatal("nil request in queue");
803 sf = r->req->fid != nil ? r->req->fid->aux : nil;
804 switch(r->req->ifcall.type){
806 sendpkt("bus", SSH_FXP_STAT, r->reqid, sf->fn, strlen(sf->fn));
809 sendpkt("bus", SSH_FXP_STAT, r->reqid, r->req->aux, strlen(r->req->aux));
813 if((r->req->ofcall.qid.type & QTDIR) != 0)
814 sendpkt("bus", SSH_FXP_OPENDIR, r->reqid, sf->fn, strlen(sf->fn));
816 x = r->req->ifcall.mode;
819 case OREAD: y = SSH_FXF_READ; break;
820 case OWRITE: y = SSH_FXF_WRITE; break;
821 case ORDWR: y = SSH_FXF_READ | SSH_FXF_WRITE; break;
823 if(readonly && (y & SSH_FXF_WRITE) != 0){
824 respond(r->req, "mounted read-only");
829 if((x & OTRUNC) != 0)
831 sendpkt("busuu", SSH_FXP_OPEN, r->reqid, sf->fn, strlen(sf->fn), y, 0);
837 s = pathcat(sf->fn, r->req->ifcall.name);
839 if((r->req->ifcall.perm & DMDIR) != 0){
840 if(r->req->aux == nil){
841 sendpkt("busuu", SSH_FXP_MKDIR, r->reqid, s, strlen(s),
842 SSH_FILEXFER_ATTR_PERMISSIONS, r->req->ifcall.perm & 0777);
843 r->req->aux = (void*)-1;
845 sendpkt("bus", SSH_FXP_OPENDIR, r->reqid, s, strlen(s));
846 r->req->aux = (void*)-2;
851 x = r->req->ifcall.mode;
852 y = SSH_FXF_CREAT | SSH_FXF_EXCL;
854 case OREAD: y |= SSH_FXF_READ; break;
855 case OWRITE: y |= SSH_FXF_WRITE; break;
856 case ORDWR: y |= SSH_FXF_READ | SSH_FXF_WRITE; break;
858 sendpkt("busuuu", SSH_FXP_OPEN, r->reqid, s, strlen(s), y,
859 SSH_FILEXFER_ATTR_PERMISSIONS, r->req->ifcall.perm & 0777);
863 if((r->req->fid->qid.type & QTDIR) != 0){
865 if(r->req->aux == (void*)-1){
866 sendpkt("bus", SSH_FXP_CLOSE, r->reqid, sf->hand, sf->handn);
872 }else if(r->req->aux == (void*)-2){
873 sendpkt("bus", SSH_FXP_OPENDIR, r->reqid, sf->fn, strlen(sf->fn));
876 sendpkt("bus", SSH_FXP_READDIR, r->reqid, sf->hand, sf->handn);
881 sendpkt("busvuu", SSH_FXP_READ, r->reqid, sf->hand, sf->handn,
882 r->req->ifcall.offset, r->req->ifcall.count);
887 x = r->req->ifcall.count - r->req->ofcall.count;
888 if(x >= MAXWRITE) x = MAXWRITE;
890 sendpkt("busvs", SSH_FXP_WRITE, r->reqid, sf->hand, sf->handn,
891 r->req->ifcall.offset + r->req->ofcall.count,
892 r->req->ifcall.data + r->req->ofcall.count,
895 r->req->ofcall.offset = x;
899 r->req->d.name = finalelem(sf->fn);
900 r->req->d.qid = sf->qid;
901 if(sf->handn > 0 && (sf->qid.type & QTDIR) == 0)
902 sendpkt("bus", SSH_FXP_FSTAT, r->reqid, sf->hand, sf->handn);
904 sendpkt("bus", SSH_FXP_STAT, r->reqid, sf->fn, strlen(sf->fn));
908 if(r->req->aux == (void *) -1){
910 s = parentdir(sf->fn);
911 t = pathcat(s, r->req->d.name);
914 sendpkt("buss", SSH_FXP_RENAME, r->reqid, sf->fn, strlen(sf->fn), t, strlen(t));
918 x = dir2attrib(&r->req->d, (uchar **) &s);
920 responderror(r->req);
926 sendpkt("bus[", SSH_FXP_FSETSTAT, r->reqid, sf->hand, sf->handn, s, x);
928 sendpkt("bus[", SSH_FXP_SETSTAT, r->reqid, sf->fn, strlen(sf->fn), s, x);
934 if((sf->qid.type & QTDIR) != 0)
935 sendpkt("bus", SSH_FXP_RMDIR, r->reqid, sf->fn, strlen(sf->fn));
937 sendpkt("bus", SSH_FXP_REMOVE, r->reqid, sf->fn, strlen(sf->fn));
941 fprint(2, "sendproc: unimplemented 9p request %F in queue\n", &r->req->ifcall);
942 respond(r->req, "phase error");
951 static char ebuf[256];
957 char *msg, *lang, *hand, *s;
958 int msgn, langn, handn;
962 threadsetname("recv");
966 switch(t = recvpkt()){
974 fprint(2, "sshfs: received unexpected packet of type %Σ\n", t);
977 id = GET4(rxpkt + 1);
979 fprint(2, "sshfs: received %Σ response with id out of range, %d > %d\n", t, id, MAXREQID);
987 rwakeup(&sreqidrend);
989 qunlock(&sreqidlock);
991 fprint(2, "sshfs: received %Σ response to non-existent request (req id = %d)\n", t, id);
994 if(r->closefid != nil){
999 sysfatal("recvproc: r->req == nil");
1001 sf = r->req->fid != nil ? r->req->fid->aux : nil;
1002 okresp = rxlen >= 9 && t == SSH_FXP_STATUS && GET4(rxpkt+5) == SSH_FX_OK;
1003 switch(r->req->ifcall.type){
1005 if(t != SSH_FXP_ATTRS) goto common;
1006 if(attrfixupqid(&r->req->ofcall.qid) < 0)
1008 r->req->aux = (void*)-1;
1009 if((r->req->ofcall.qid.type & QTDIR) == 0)
1010 respond(r->req, "not a directory");
1012 sshfsattach(r->req);
1015 if(t != SSH_FXP_ATTRS) goto common;
1016 if(r->req->ofcall.nwqid <= 0
1017 || attrfixupqid(&r->req->ofcall.wqid[r->req->ofcall.nwqid - 1]) < 0)
1019 walkprocess(r->req, nil);
1022 if(okresp && r->req->aux == (void*)-1){
1027 case Topen: opendir:
1028 if(t != SSH_FXP_HANDLE) goto common;
1029 if(unpack(rxpkt, rxlen, "_____s", &hand, &handn) < 0) goto garbage;
1032 sf->hand = emalloc9p(sf->handn);
1033 memcpy(sf->hand, hand, sf->handn);
1034 if(r->req->ifcall.type == Tcreate){
1036 sf->fn = pathcat(s, r->req->ifcall.name);
1038 sf->qid = (Qid){qidcalc(sf->fn), 0, (r->req->ifcall.perm & DMDIR) != 0 ? QTDIR : 0};
1039 r->req->ofcall.qid = sf->qid;
1040 r->req->fid->qid = sf->qid;
1043 if(r->req->ifcall.type == Tread){
1045 readprocess(r->req);
1047 respond(r->req, nil);
1050 if((r->req->fid->qid.type & QTDIR) != 0){
1051 if(r->req->aux == (void*)-1){
1052 if(t != SSH_FXP_STATUS) goto common;
1053 /* reopen even if close failed */
1054 r->req->aux = (void*)-2;
1056 }else if(r->req->aux == (void*)-2)
1059 if(t != SSH_FXP_NAME) goto common;
1060 if(parsedir(sf) < 0) goto garbage;
1061 readprocess(r->req);
1065 if(t != SSH_FXP_DATA) goto common;
1066 if(unpack(rxpkt, rxlen, "_____s", &msg, &msgn) < 0) goto garbage;
1067 if(msgn > r->req->ifcall.count) msgn = r->req->ifcall.count;
1068 r->req->ofcall.count = msgn;
1069 memcpy(r->req->ofcall.data, msg, msgn);
1070 respond(r->req, nil);
1073 if(t != SSH_FXP_STATUS) goto common;
1075 r->req->ofcall.count += r->req->ofcall.offset;
1076 if(r->req->ofcall.count == r->req->ifcall.count)
1077 respond(r->req, nil);
1082 if(r->req->ofcall.count == 0) goto common;
1083 respond(r->req, nil);
1086 if(t != SSH_FXP_ATTRS) goto common;
1087 if(attrib2dir(rxpkt + 5, rxpkt + rxlen, &r->req->d) < 0) goto garbage;
1088 respond(r->req, nil);
1091 if(!okresp) goto common;
1092 if(!r->req->d.name[0]){
1093 respond(r->req, nil);
1096 if(r->req->aux == nil){
1097 r->req->aux = (void *) -1;
1102 sf->fn = r->req->aux;
1104 respond(r->req, nil);
1110 fprint(2, "sendproc: unimplemented 9p request %F in queue\n", &r->req->ifcall);
1111 respond(r->req, "phase error");
1118 case SSH_FXP_STATUS:
1119 if(unpack(rxpkt, rxlen, "_____uss", &code, &msg, &msgn, &lang, &langn) < 0){
1121 fprint(2, "sshfs: garbled packet in response to 9p request %F\n", &r->req->ifcall);
1124 if(code == SSH_FX_OK)
1126 else if(code == SSH_FX_EOF && r->req->ifcall.type == Tread){
1127 if((r->req->fid->qid.type & QTDIR) != 0){
1131 readprocess(r->req);
1135 r->req->ofcall.count = 0;
1140 }else if(code < nelem(errors))
1143 snprint(ebuf, sizeof(ebuf), "error code %d", code);
1148 fprint(2, "sshfs: received unexpected packet %Σ for 9p request %F\n", t, &r->req->ifcall);
1150 if(r->req->ifcall.type == Twalk)
1151 walkprocess(r->req, e);
1166 if(r->fid != r->newfid){
1167 r->newfid->qid = r->fid->qid;
1169 t = emalloc9p(sizeof(SFid));
1170 t->fn = estrdup9p(s->fn);
1175 if(r->ifcall.nwname == 0){
1179 p = estrdup9p(t->fn);
1180 for(i = 0; i < r->ifcall.nwname; i++){
1181 q = pathcat(p, r->ifcall.wname[i]);
1184 r->ofcall.wqid[i] = (Qid){qidcalc(p), 0, QTDIR};
1186 r->ofcall.nwqid = r->ifcall.nwname;
1192 sshfsdestroyfid(Fid *f)
1200 if(sf->hand != nil){
1201 sr = emalloc9p(sizeof(SReq));
1210 sshfsdestroyreq(Req *r)
1212 if(r->ifcall.type == Twalk)
1219 proccreate(sendproc, nil, mainstacksize);
1220 proccreate(recvproc, nil, mainstacksize);
1226 dprint("sshfs: ending\n");
1227 threadexitsall(nil);
1232 .attach sshfsattach,
1241 .destroyfid sshfsdestroyfid,
1242 .destroyreq sshfsdestroyreq,
1255 if(fn == nil) return nil;
1256 sendpkt("busuu", SSH_FXP_OPEN, 0, fn, strlen(fn), SSH_FXF_READ, 0);
1257 if(recvpkt() != SSH_FXP_HANDLE) return nil;
1258 if(unpack(rxpkt, rxlen, "_____s", &dat, &handn) < 0) return nil;
1259 hand = emalloc9p(handn);
1260 memcpy(hand, dat, handn);
1264 sendpkt("busvu", SSH_FXP_READ, 0, hand, handn, (uvlong)off, MAXWRITE);
1266 case SSH_FXP_STATUS:
1267 if(unpack(rxpkt, rxlen, "_____u", &code) < 0) goto err;
1268 if(code == SSH_FX_EOF) goto out;
1272 if(unpack(rxpkt, rxlen, "_____s", &dat, &datn) < 0) goto err;
1275 p = erealloc9p(p, off + datn + 1);
1276 memcpy(p + off, dat, datn);
1283 sendpkt("bus", SSH_FXP_CLOSE, 0, hand, handn);
1290 passwdparse(IDEnt **tab, char *s)
1300 p = strpbrk(p, ":\n"); if(p == nil) break; if(*p != ':'){ p++; continue; }
1302 p = strpbrk(p+1, ":\n");
1303 p = strpbrk(p, ":\n"); if(p == nil) break; if(*p != ':'){ p++; continue; }
1304 id = strtol(p+1, &p, 10);
1305 p = strchr(p, '\n');
1308 e = emalloc9p(sizeof(IDEnt));
1309 e->name = estrdup9p(n);
1311 b = &tab[((ulong)e->id) % HASH];
1331 if(strncmp(sshargv[0], "./", 2) != 0)
1332 f = smprint("/bin/%s", sshargv[0]);
1335 procexec(nil, f, sshargv);
1336 sysfatal("exec: %r");
1342 static char *common = "[-abdRUG] [-s service] [-m mtpt] [-u uidfile] [-g gidfile]";
1343 fprint(2, "usage: %s %s [-- ssh-options] [user@]host\n", argv0, common);
1344 fprint(2, " %s %s -c cmdline\n", argv0, common);
1345 fprint(2, " %s %s -p\n", argv0, common);
1350 threadmain(int argc, char **argv)
1353 static int pflag, cflag;
1354 static char *svc, *mtpt;
1356 static char *uidfile, *gidfile;
1358 fmtinstall(L'Σ', fxpfmt);
1361 uidfile = "/etc/passwd";
1362 gidfile = "/etc/group";
1364 case 'R': readonly++; break;
1365 case 'd': debug++; chatty9p++; break;
1366 case 'p': pflag++; break;
1367 case 'c': cflag++; break;
1368 case 's': svc = EARGF(usage()); break;
1369 case 'a': mflag |= MAFTER; break;
1370 case 'b': mflag |= MBEFORE; break;
1371 case 'm': mtpt = EARGF(usage()); break;
1372 case 'M': mtpt = nil; break;
1373 case 'u': uidfile = EARGF(usage()); break;
1374 case 'U': uidfile = nil; break;
1375 case 'g': gidfile = EARGF(usage()); break;
1376 case 'G': gidfile = nil; break;
1377 case 'r': root = EARGF(usage()); break;
1382 sshfssrv.create = nil;
1383 sshfssrv.write = nil;
1384 sshfssrv.wstat = nil;
1385 sshfssrv.remove = nil;
1392 if(argc == 0) usage();
1398 sshargv = emalloc9p(sizeof(char *) * (sshargc + 1));
1400 memcpy(sshargv + 1, argv, argc * sizeof(char *));
1401 sshargv[sshargc - 1] = "#sftp";
1404 rdfd = wrfd = pfd[0];
1405 procrfork(startssh, nil, mainstacksize, RFFDG|RFNOTEG|RFNAMEG);
1409 sendpkt("bu", SSH_FXP_INIT, VERSION);
1410 if(recvpkt() != SSH_FXP_VERSION || unpack(rxpkt, rxlen, "_u", &x) < 0) sysfatal("received garbage");
1411 if(x != VERSION) sysfatal("server replied with incompatible version %d", x);
1413 passwdparse(uidtab, readfile(uidfile));
1414 passwdparse(gidtab, readfile(gidfile));
1416 threadpostmountsrv(&sshfssrv, svc, mtpt, MCREATE | mflag);