]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/sshfs.c
merge
[plan9front.git] / sys / src / cmd / sshfs.c
index cb5692d10fc0de8f5a5c8b6871df342f1dd94d39..30638ddb6400f9dea88e354793ed25ebe2e20a53 100644 (file)
@@ -7,6 +7,7 @@
 
 int readonly;
 int debug;
+char *root = ".";
 #define dprint(...) if(debug) fprint(2, __VA_ARGS__)
 #pragma        varargck        type    "Σ"    int
 
@@ -102,7 +103,6 @@ struct SFid {
 struct SReq {
        Req *req;
        SFid *closefid;
-       int reqid;
        SReq *next;
 };
 
@@ -171,7 +171,7 @@ idlookup(IDEnt **tab, int id)
        
        for(p = tab[(ulong)id % HASH]; p != nil; p = p->next)
                if(p->id == id)
-                       return strdup(p->name);
+                       return estrdup9p(p->name);
        return smprint("%d", id);
 }
 
@@ -192,10 +192,30 @@ namelookup(IDEnt **tab, char *name)
        return -1;
 }
 
+int
+allocsreqid(SReq *r)
+{
+       int i;
+
+       qlock(&sreqidlock);
+       for(;;){
+               for(i = 0; i < MAXREQID; i++)
+                       if(sreqrd[i] == nil){
+                               sreqrd[i] = r;
+                               goto out;
+                       }
+               rsleep(&sreqidrend);
+       }
+out:
+       qunlock(&sreqidlock);
+       return i;
+}
+
 int
 vpack(uchar *p, int n, char *fmt, va_list a)
 {
        uchar *p0 = p, *e = p+n;
+       SReq *sr = nil;
        u32int u;
        u64int v;
        void *s;
@@ -204,6 +224,10 @@ vpack(uchar *p, int n, char *fmt, va_list a)
        for(;;){
                switch(c = *fmt++){
                case '\0':
+                       if(sr != nil){
+                               u = allocsreqid(sr);
+                               PUT4(p0+1, u);
+                       }
                        return p - p0;
                case '_':
                        if(++p > e) goto err;
@@ -227,6 +251,11 @@ vpack(uchar *p, int n, char *fmt, va_list a)
                        memmove(p, s, u);
                        p += u;
                        break;
+               case 'q':
+                       p += 4;
+                       if(p != p0+5 || p > e) goto err;
+                       sr = va_arg(a, SReq*);
+                       break;
                case 'u':
                        u = va_arg(a, int);
                        if(p+4 > e) goto err;
@@ -366,30 +395,36 @@ recvpkt(void)
        return rxpkt[0];
 }
 
+void
+freedir1(Dir *d)
+{
+       free(d->name);
+       free(d->uid);
+       free(d->gid);
+       free(d->muid);
+}
+
 void
 freedir(SFid *s)
 {
        int i;
-       Dir *d;
 
-       for(i = 0; i < s->ndirent; i++){
-               d = &s->dirent[i];
-               free(d->name);
-               free(d->uid);
-               free(d->gid);
-               free(d->muid);
-       }
+       for(i = 0; i < s->ndirent; i++)
+               freedir1(&s->dirent[i]);
        free(s->dirent);
        s->dirent = nil;
        s->ndirent = 0;
        s->dirpos = 0;
 }
 
-
 void
 putsfid(SFid *s)
 {
        if(s == nil) return;
+
+       wlock(s);
+       wunlock(s);
+
        free(s->fn);
        free(s->hand);
        freedir(s);
@@ -399,14 +434,6 @@ putsfid(SFid *s)
 void
 putsreq(SReq *s)
 {
-       if(s == nil) return;
-       if(s->reqid != -1){
-               qlock(&sreqidlock);
-               sreqrd[s->reqid] = nil;
-               rwakeup(&sreqidrend);
-               qunlock(&sreqidlock);
-       }
-       putsfid(s->closefid);
        free(s);
 }
 
@@ -420,14 +447,12 @@ submitsreq(SReq *s)
        qunlock(&sreqwrlock);
 }
 
-
 void
 submitreq(Req *r)
 {
        SReq *s;
        
        s = emalloc9p(sizeof(SReq));
-       s->reqid = -1;
        s->req = r;
        submitsreq(s);
 }
@@ -435,23 +460,13 @@ submitreq(Req *r)
 char *
 pathcat(char *p, char *c)
 {
-       if(strcmp(p, ".") == 0)
-               return strdup(c);
-       return smprint("%s/%s", p, c);
+       return cleanname(smprint("%s/%s", p, c));
 }
 
 char *
 parentdir(char *p)
 {
-       char *q, *r;
-       
-       if(strcmp(p, ".") == 0) return strdup(".");
-       if(strcmp(p, "/") == 0) return strdup("/");
-       q = strdup(p);
-       r = strrchr(q, '/');
-       if(r != nil) *r = 0;
-       else strcpy(q, ".");
-       return q;
+       return pathcat(p, "..");
 }
 
 char *
@@ -460,8 +475,8 @@ finalelem(char *p)
        char *q;
        
        q = strrchr(p, '/');
-       if(q == nil) return strdup(p);
-       return strdup(q+1);
+       if(q == nil) return estrdup9p(p);
+       return estrdup9p(q+1);
 }
 
 u64int
@@ -474,7 +489,7 @@ qidcalc(char *c)
 }
 
 void
-walkprocess(Req *r, int isdir, char *e)
+walkprocess(Req *r, char *e)
 {
        char *p;
        SFid *sf;
@@ -492,8 +507,6 @@ walkprocess(Req *r, int isdir, char *e)
                submitreq(r);
        }else{
                assert(r->ofcall.nwqid > 0);
-               if(!isdir)
-                       r->ofcall.wqid[r->ofcall.nwqid - 1].type = 0;
                wlock(sf);
                free(sf->fn);
                sf->fn = r->aux;
@@ -523,10 +536,10 @@ attrib2dir(uchar *p0, uchar *ep, Dir *d)
                d->uid = idlookup(uidtab, uid);
                d->gid = idlookup(gidtab, gid);
        }else{
-               d->uid = strdup("sshfs");
-               d->gid = strdup("sshfs");
+               d->uid = estrdup9p("sshfs");
+               d->gid = estrdup9p("sshfs");
        }
-       d->muid = strdup(d->uid);
+       d->muid = estrdup9p(d->uid);
        if((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0){
                rc = unpack(p, ep - p, "u", &perm); if(rc < 0) return -1; p += rc;
                d->mode = perm & 0777;
@@ -535,6 +548,7 @@ attrib2dir(uchar *p0, uchar *ep, Dir *d)
        d->qid.type = d->mode >> 24;
        if((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0){
                rc = unpack(p, ep - p, "uu", &d->atime, &d->mtime); if(rc < 0) return -1; p += rc;
+               d->qid.vers = d->mtime;
        }
        if((flags & SSH_FILEXFER_ATTR_EXTENDED) != 0){
                rc = unpack(p, ep - p, "u", &next); if(rc < 0) return -1; p += rc;
@@ -553,9 +567,9 @@ dir2attrib(Dir *d, uchar **rp)
        uchar *r, *p, *e;
        u32int fl;
        int uid, gid;
-       
+
        werrstr("phase error");
-       r = emalloc9p(MAXATTRIB);
+       *rp = r = emalloc9p(MAXATTRIB);
        e = r + MAXATTRIB;
        fl = 0;
        p = r + 4;
@@ -590,26 +604,31 @@ dir2attrib(Dir *d, uchar **rp)
                rc = pack(p, e - p, "uu", d->atime, d->mtime); if(rc < 0) return -1; p += rc;
        }
        PUT4(r, fl);
-       *rp = r;
        return p - r;
 }
 
 int
-attribisdir(char *fn)
+attrfixupqid(Qid *qid)
 {
-       u32int code;
+       u32int flags;
        uchar *p;
-       
-       if(unpack(rxpkt, rxlen, "_____u", &code) < 0) return -1;
-       if((code & 4) == 0){
-               fprint(2, "sshfs: can't determine if %s is a directory\n", fn);
-               return 1;
-       }
+
+       if(unpack(rxpkt, rxlen, "_____u", &flags) < 0) return -1;
        p = rxpkt + 9;
-       if(code & 1) p += 8;
-       if(code & 2) p += 8;
-       if(p + 4 > rxpkt + rxlen) return -1;
-       return (GET4(p) & 0170000) == 0040000;
+       if(flags & SSH_FILEXFER_ATTR_SIZE) p += 8;
+       if(flags & SSH_FILEXFER_ATTR_UIDGID) p += 8;
+       if(flags & SSH_FILEXFER_ATTR_PERMISSIONS){
+               if(p + 4 > rxpkt + rxlen) return -1;
+               if((GET4(p) & 0170000) != 0040000) qid->type = 0;
+               else qid->type = QTDIR;
+               p += 4;
+       }
+       if(flags & SSH_FILEXFER_ATTR_ACMODTIME){
+               if(p + 8 > rxpkt + rxlen) return -1;
+               p += 4;
+               qid->vers = GET4(p);    /* mtime for qid.vers */
+       }
+       return 0;
 }
 
 int
@@ -631,17 +650,16 @@ parsedir(SFid *sf)
        p = rxpkt + 9;
        ep = rxpkt + rxlen;
        for(i = 0; i < c; i++){
-               rc = unpack(p, ep - p, "ss", &fn, &fns, &ln, &lns); if(rc < 0) goto err; p += rc;
                memset(d, 0, sizeof(Dir));
+               rc = unpack(p, ep - p, "ss", &fn, &fns, &ln, &lns); if(rc < 0) goto err; p += rc;
                rc = attrib2dir(p, ep, d); if(rc < 0) goto err; p += rc;
                if(fn[0] == '.' && (fns == 1 || fns == 2 && fn[1] == '.')){
-                       free(d->uid);
-                       free(d->gid);
-                       free(d->muid);
+                       freedir1(d);
                        continue;
                }
                d->name = emalloc9p(fns + 1);
                memcpy(d->name, fn, fns);
+               d->name[fns] = 0;
                s = pathcat(sf->fn, d->name);
                d->qid.path = qidcalc(s);
                free(s);
@@ -651,6 +669,7 @@ parsedir(SFid *sf)
        wunlock(sf);
        return 0;
 err:
+       freedir1(d);
        wunlock(sf);
        return -1;
 }
@@ -698,20 +717,18 @@ readprocess(Req *r)
 void
 sshfsread(Req *r)
 {
-       SFid *sf;
-
        if((r->fid->qid.type & QTDIR) == 0){
                submitreq(r);
                return;
        }
-       sf = r->fid->aux;       
        if(r->ifcall.offset == 0){
+               SFid *sf = r->fid->aux;
                wlock(sf);
                freedir(sf);
                if(sf->dirreads > 0){
+                       wunlock(sf);
                        r->aux = (void*)-1;
                        submitreq(r);
-                       wunlock(sf);
                        return;
                }
                wunlock(sf);
@@ -724,20 +741,40 @@ sshfsattach(Req *r)
 {
        SFid *sf;
 
-       if(r->ifcall.aname != nil && *r->ifcall.aname != 0 && r->aux == nil){
+       if(r->aux == nil){
+               sf = emalloc9p(sizeof(SFid));
+               if(r->ifcall.aname != nil)
+                       switch(*r->ifcall.aname){
+                       case '~':
+                               switch(r->ifcall.aname[1]){
+                               case 0: sf->fn = estrdup9p("."); break;
+                               case '/': sf->fn = estrdup9p(r->ifcall.aname + 2); break;
+                               default:
+                                       free(sf);
+                                       respond(r, "invalid attach name");
+                                       return;
+                               }
+                               break;
+                       case '/':
+                               sf->fn = estrdup9p(r->ifcall.aname);
+                               break;
+                       case 0:
+                               sf->fn = estrdup9p(root);
+                               break;
+                       default:
+                               sf->fn = pathcat(root, r->ifcall.aname);
+                       }
+               else
+                       sf->fn = estrdup9p(root);
+               r->fid->aux = sf;
                submitreq(r);
-               return;
+       }else{
+               sf = r->fid->aux;
+               sf->qid = (Qid){qidcalc(sf->fn), 0, QTDIR};
+               r->ofcall.qid = sf->qid;
+               r->fid->qid = sf->qid;
+               respond(r, nil);
        }
-       sf = emalloc9p(sizeof(SFid));
-       if(r->ifcall.aname != nil && *r->ifcall.aname != 0)
-               sf->fn = strdup(r->ifcall.aname);
-       else
-               sf->fn = strdup(".");
-       sf->qid = (Qid){qidcalc(sf->fn), 0, QTDIR};
-       r->ofcall.qid = sf->qid;
-       r->fid->qid = sf->qid;
-       r->fid->aux = sf;
-       respond(r, nil);
 }
 
 void
@@ -745,7 +782,6 @@ sendproc(void *)
 {
        SReq *r;
        SFid *sf;
-       int i;
        int x, y;
        char *s, *t;
 
@@ -759,41 +795,33 @@ sendproc(void *)
                sreqwr = r->next;
                if(sreqwr == nil) sreqlast = &sreqwr;
                qunlock(&sreqwrlock);
-               
-               qlock(&sreqidlock);
-       idagain:
-               for(i = 0; i < MAXREQID; i++)
-                       if(sreqrd[i] == nil){
-                               sreqrd[i] = r;
-                               r->reqid = i;
-                               break;
-                       }
-               if(i == MAXREQID){
-                       rsleep(&sreqidrend);
-                       goto idagain;
-               }
-               qunlock(&sreqidlock);
 
-               if(r->closefid != nil){
-                       sendpkt("bus", SSH_FXP_CLOSE, r->reqid, r->closefid->hand, r->closefid->handn);
+               sf = r->closefid;
+               if(sf != nil){
+                       rlock(sf);
+                       sendpkt("bqs", SSH_FXP_CLOSE, r, sf->hand, sf->handn);
+                       runlock(sf);
                        continue;
                }
                if(r->req == nil)
                        sysfatal("nil request in queue");
 
-               sf = r->req->fid != nil ? r->req->fid->aux : nil;
+               sf = r->req->fid->aux;
                switch(r->req->ifcall.type){
                case Tattach:
-                       sendpkt("bus", SSH_FXP_STAT, r->reqid, r->req->ifcall.aname, strlen(r->req->ifcall.aname));
+                       rlock(sf);
+                       sendpkt("bqs", SSH_FXP_STAT, r, sf->fn, strlen(sf->fn));
+                       runlock(sf);
                        break;
                case Twalk:
-                       sendpkt("bus", SSH_FXP_STAT, r->reqid, r->req->aux, strlen(r->req->aux));
+                       sendpkt("bqs", SSH_FXP_STAT, r, r->req->aux, strlen(r->req->aux));
                        break;
                case Topen:
-                       rlock(sf);
-                       if((r->req->ofcall.qid.type & QTDIR) != 0)
-                               sendpkt("bus", SSH_FXP_OPENDIR, r->reqid, sf->fn, strlen(sf->fn));
-                       else{
+                       if((r->req->ofcall.qid.type & QTDIR) != 0){
+                               rlock(sf);
+                               sendpkt("bqs", SSH_FXP_OPENDIR, r, sf->fn, strlen(sf->fn));
+                               runlock(sf);
+                       }else{
                                x = r->req->ifcall.mode;
                                y = 0;
                                switch(x & 3){
@@ -803,15 +831,15 @@ sendproc(void *)
                                }
                                if(readonly && (y & SSH_FXF_WRITE) != 0){
                                        respond(r->req, "mounted read-only");
-                                       runlock(sf);
                                        putsreq(r);
                                        break;
                                }
                                if((x & OTRUNC) != 0)
                                        y |= SSH_FXF_TRUNC;
-                               sendpkt("busuu", SSH_FXP_OPEN, r->reqid, sf->fn, strlen(sf->fn), y, 0);
+                               rlock(sf);
+                               sendpkt("bqsuu", SSH_FXP_OPEN, r, sf->fn, strlen(sf->fn), y, 0);
+                               runlock(sf);
                        }
-                       runlock(sf);
                        break;
                case Tcreate:
                        rlock(sf);
@@ -819,12 +847,12 @@ sendproc(void *)
                        runlock(sf);
                        if((r->req->ifcall.perm & DMDIR) != 0){
                                if(r->req->aux == nil){
-                                       sendpkt("busuu", SSH_FXP_MKDIR, r->reqid, s, strlen(s),
-                                               SSH_FILEXFER_ATTR_PERMISSIONS, r->req->ifcall.perm & 0777);
                                        r->req->aux = (void*)-1;
+                                       sendpkt("bqsuu", SSH_FXP_MKDIR, r, s, strlen(s),
+                                               SSH_FILEXFER_ATTR_PERMISSIONS, r->req->ifcall.perm & 0777);
                                }else{
-                                       sendpkt("bus", SSH_FXP_OPENDIR, r->reqid, s, strlen(s));
                                        r->req->aux = (void*)-2;
+                                       sendpkt("bqs", SSH_FXP_OPENDIR, r, s, strlen(s));
                                }
                                free(s);
                                break;
@@ -836,7 +864,7 @@ sendproc(void *)
                        case OWRITE: y |= SSH_FXF_WRITE; break;
                        case ORDWR: y |= SSH_FXF_READ | SSH_FXF_WRITE; break;
                        }
-                       sendpkt("busuuu", SSH_FXP_OPEN, r->reqid, s, strlen(s), y,
+                       sendpkt("bqsuuu", SSH_FXP_OPEN, r, s, strlen(s), y,
                                SSH_FILEXFER_ATTR_PERMISSIONS, r->req->ifcall.perm & 0777);
                        free(s);
                        break;
@@ -844,22 +872,22 @@ sendproc(void *)
                        if((r->req->fid->qid.type & QTDIR) != 0){
                                wlock(sf);
                                if(r->req->aux == (void*)-1){
-                                       sendpkt("bus", SSH_FXP_CLOSE, r->reqid, sf->hand, sf->handn);
+                                       sendpkt("bqs", SSH_FXP_CLOSE, r, sf->hand, sf->handn);
                                        free(sf->hand);
                                        sf->hand = nil;
                                        sf->handn = 0;
                                        sf->direof = 0;
                                        sf->dirreads = 0;
                                }else if(r->req->aux == (void*)-2){
-                                       sendpkt("bus", SSH_FXP_OPENDIR, r->reqid, sf->fn, strlen(sf->fn));
+                                       sendpkt("bqs", SSH_FXP_OPENDIR, r, sf->fn, strlen(sf->fn));
                                }else{
-                                       sendpkt("bus", SSH_FXP_READDIR, r->reqid, sf->hand, sf->handn);
                                        sf->dirreads++;
+                                       sendpkt("bqs", SSH_FXP_READDIR, r, sf->hand, sf->handn);
                                }
                                wunlock(sf);
                        }else{
                                rlock(sf);
-                               sendpkt("busvuu", SSH_FXP_READ, r->reqid, sf->hand, sf->handn,
+                               sendpkt("bqsvuu", SSH_FXP_READ, r, sf->hand, sf->handn,
                                        r->req->ifcall.offset, r->req->ifcall.count);
                                runlock(sf);
                        }
@@ -867,54 +895,56 @@ sendproc(void *)
                case Twrite:
                        x = r->req->ifcall.count - r->req->ofcall.count;
                        if(x >= MAXWRITE) x = MAXWRITE;
+                       r->req->ofcall.offset = x;
                        rlock(sf);
-                       sendpkt("busvs", SSH_FXP_WRITE, r->reqid, sf->hand, sf->handn,
+                       sendpkt("bqsvs", SSH_FXP_WRITE, r, sf->hand, sf->handn,
                                r->req->ifcall.offset + r->req->ofcall.count,
                                r->req->ifcall.data + r->req->ofcall.count,
                                x);
                        runlock(sf);
-                       r->req->ofcall.offset = x;
                        break;
                case Tstat:
                        rlock(sf);
                        r->req->d.name = finalelem(sf->fn);
                        r->req->d.qid = sf->qid;
                        if(sf->handn > 0 && (sf->qid.type & QTDIR) == 0)
-                               sendpkt("bus", SSH_FXP_FSTAT, r->reqid, sf->hand, sf->handn);
+                               sendpkt("bqs", SSH_FXP_FSTAT, r, sf->hand, sf->handn);
                        else
-                               sendpkt("bus", SSH_FXP_STAT, r->reqid, sf->fn, strlen(sf->fn));
+                               sendpkt("bqs", SSH_FXP_STAT, r, sf->fn, strlen(sf->fn));
                        runlock(sf);
                        break;
                case Twstat:
-                       if(r->req->aux == (void *) -1){
+                       if(r->req->aux == (void*)-1){
                                rlock(sf);
                                s = parentdir(sf->fn);
                                t = pathcat(s, r->req->d.name);
-                               sendpkt("buss", SSH_FXP_RENAME, r->reqid, sf->fn, strlen(sf->fn), t, strlen(t));
-                               free(s);
                                r->req->aux = t;
+                               sendpkt("bqss", SSH_FXP_RENAME, r, sf->fn, strlen(sf->fn), t, strlen(t));
                                runlock(sf);
+                               free(s);
                                break;
                        }
                        x = dir2attrib(&r->req->d, (uchar **) &s);
                        if(x < 0){
                                responderror(r->req);
                                putsreq(r);
+                               free(s);
                                break;
                        }
                        rlock(sf);
                        if(sf->handn > 0)
-                               sendpkt("bus[", SSH_FXP_FSETSTAT, r->reqid, sf->hand, sf->handn, s, x);
+                               sendpkt("bqs[", SSH_FXP_FSETSTAT, r, sf->hand, sf->handn, s, x);
                        else
-                               sendpkt("bus[", SSH_FXP_SETSTAT, r->reqid, sf->fn, strlen(sf->fn), s, x);
+                               sendpkt("bqs[", SSH_FXP_SETSTAT, r, sf->fn, strlen(sf->fn), s, x);
                        runlock(sf);
+                       free(s);
                        break;
                case Tremove:
                        rlock(sf);
                        if((sf->qid.type & QTDIR) != 0)
-                               sendpkt("bus", SSH_FXP_RMDIR, r->reqid, sf->fn, strlen(sf->fn));
+                               sendpkt("bqs", SSH_FXP_RMDIR, r, sf->fn, strlen(sf->fn));
                        else
-                               sendpkt("bus", SSH_FXP_REMOVE, r->reqid, sf->fn, strlen(sf->fn));
+                               sendpkt("bqs", SSH_FXP_REMOVE, r, sf->fn, strlen(sf->fn));
                        runlock(sf);
                        break;
                default:
@@ -932,9 +962,9 @@ recvproc(void *)
 
        SReq *r;
        SFid *sf;
-       int t, id, rc;
+       int t, id;
        u32int code;
-       char *msg, *lang, *hand;
+       char *msg, *lang, *hand, *s;
        int msgn, langn, handn;
        int okresp;
        char *e;
@@ -963,7 +993,6 @@ recvproc(void *)
                r = sreqrd[id];
                if(r != nil){
                        sreqrd[id] = nil;
-                       r->reqid = -1;
                        rwakeup(&sreqidrend);
                }
                qunlock(&sreqidlock);
@@ -972,31 +1001,32 @@ recvproc(void *)
                        continue;
                }
                if(r->closefid != nil){
+                       putsfid(r->closefid);
                        putsreq(r);
                        continue;
                }
                if(r->req == nil)
                        sysfatal("recvproc: r->req == nil");
 
-               sf = r->req->fid != nil ? r->req->fid->aux : nil;
+               sf = r->req->fid->aux;
                okresp = rxlen >= 9 && t == SSH_FXP_STATUS && GET4(rxpkt+5) == SSH_FX_OK;
                switch(r->req->ifcall.type){
                case Tattach:
                        if(t != SSH_FXP_ATTRS) goto common;
-                       rc = attribisdir(r->req->ifcall.aname);
-                       r->req->aux = (void*)-1;
-                       if(rc < 0)
+                       if(attrfixupqid(&r->req->ofcall.qid) < 0)
                                goto garbage;
-                       if(rc == 0)
+                       r->req->aux = (void*)-1;
+                       if((r->req->ofcall.qid.type & QTDIR) == 0)
                                respond(r->req, "not a directory");
                        else
                                sshfsattach(r->req);
                        break;
                case Twalk:
                        if(t != SSH_FXP_ATTRS) goto common;
-                       rc = attribisdir(((SFid*)r->req->fid)->fn);
-                       if(rc < 0) goto garbage;
-                       walkprocess(r->req, rc, nil);
+                       if(r->req->ofcall.nwqid <= 0
+                       || attrfixupqid(&r->req->ofcall.wqid[r->req->ofcall.nwqid - 1]) < 0)
+                               goto garbage;
+                       walkprocess(r->req, nil);
                        break;
                case Tcreate:
                        if(okresp && r->req->aux == (void*)-1){
@@ -1011,6 +1041,14 @@ recvproc(void *)
                        sf->handn = handn;
                        sf->hand = emalloc9p(sf->handn);
                        memcpy(sf->hand, hand, sf->handn);
+                       if(r->req->ifcall.type == Tcreate){
+                               s = sf->fn;
+                               sf->fn = pathcat(s, r->req->ifcall.name);
+                               free(s);
+                               sf->qid = (Qid){qidcalc(sf->fn), 0, (r->req->ifcall.perm & DMDIR) != 0 ? QTDIR : 0};
+                               r->req->ofcall.qid = sf->qid;
+                               r->req->fid->qid = sf->qid;
+                       }
                        wunlock(sf);
                        if(r->req->ifcall.type == Tread){
                                r->req->aux = nil;
@@ -1061,8 +1099,12 @@ recvproc(void *)
                        break;
                case Twstat:
                        if(!okresp) goto common;
+                       if(!r->req->d.name[0]){
+                               respond(r->req, nil);
+                               break;
+                       }
                        if(r->req->aux == nil){
-                               r->req->aux = (void *) -1;
+                               r->req->aux = (void*)-1;
                                submitreq(r->req);
                        }else{
                                wlock(sf);
@@ -1116,7 +1158,7 @@ recvproc(void *)
                        fprint(2, "sshfs: received unexpected packet %Σ for 9p request %F\n", t, &r->req->ifcall);
                }
                if(r->req->ifcall.type == Twalk)
-                       walkprocess(r->req, 0, e);
+                       walkprocess(r->req, e);
                else
                        respond(r->req, e);
                putsreq(r);
@@ -1135,7 +1177,7 @@ sshfswalk(Req *r)
                r->newfid->qid = r->fid->qid;
                s = r->fid->aux;
                t = emalloc9p(sizeof(SFid));
-               t->fn = strdup(s->fn);
+               t->fn = estrdup9p(s->fn);
                t->qid = s->qid;
                r->newfid->aux = t;
        }else
@@ -1144,12 +1186,9 @@ sshfswalk(Req *r)
                respond(r, nil);
                return;
        }
-       p = strdup(t->fn);
+       p = estrdup9p(t->fn);
        for(i = 0; i < r->ifcall.nwname; i++){
-               if(strcmp(r->ifcall.wname[i], "..") == 0)
-                       q = parentdir(p);
-               else
-                       q = pathcat(p, r->ifcall.wname[i]);
+               q = pathcat(p, r->ifcall.wname[i]);
                free(p);
                p = q;
                r->ofcall.wqid[i] = (Qid){qidcalc(p), 0, QTDIR};
@@ -1170,7 +1209,6 @@ sshfsdestroyfid(Fid *f)
                return;
        if(sf->hand != nil){
                sr = emalloc9p(sizeof(SReq));
-               sr->reqid = -1;
                sr->closefid = sf;
                submitsreq(sr);
        }else
@@ -1184,6 +1222,13 @@ sshfsdestroyreq(Req *r)
                free(r->aux);
 }
 
+void
+sshfsstart(Srv *)
+{
+       proccreate(sendproc, nil, mainstacksize);
+       proccreate(recvproc, nil, mainstacksize);
+}
+
 void
 sshfsend(Srv *)
 {
@@ -1192,6 +1237,7 @@ sshfsend(Srv *)
 }
 
 Srv sshfssrv = {
+       .start sshfsstart,
        .attach sshfsattach,
        .walk sshfswalk,
        .open submitreq,
@@ -1203,7 +1249,7 @@ Srv sshfssrv = {
        .remove submitreq,
        .destroyfid sshfsdestroyfid,
        .destroyreq sshfsdestroyreq,
-       .end sshfsend
+       .end sshfsend,
 };
 
 char *
@@ -1252,13 +1298,13 @@ out:
 void
 passwdparse(IDEnt **tab, char *s)
 {
-       char *p;
-       char *n;
-       int id;
        IDEnt *e, **b;
+       char *p, *n;
+       int id;
 
-       p = s;
-       for(;;){
+       if(s == nil)
+               return;
+       for(p = s;;){
                n = p;
                p = strpbrk(p, ":\n"); if(p == nil) break; if(*p != ':'){ p++; continue; }
                *p = 0;
@@ -1269,7 +1315,7 @@ passwdparse(IDEnt **tab, char *s)
                if(p == nil) break;
                p++;
                e = emalloc9p(sizeof(IDEnt));
-               e->name = strdup(n);
+               e->name = estrdup9p(n);
                e->id = id;
                b = &tab[((ulong)e->id) % HASH];
                e->next = *b;
@@ -1303,10 +1349,10 @@ void
 usage(void)
 {
        static char *common = "[-abdRUG] [-s service] [-m mtpt] [-u uidfile] [-g gidfile]";
-       fprint(2, "usage: %s %s [-- ssh-options] host\n", argv0, common);
+       fprint(2, "usage: %s %s [-- ssh-options] [user@]host\n", argv0, common);
        fprint(2, "       %s %s -c cmdline\n", argv0, common);
        fprint(2, "       %s %s -p\n", argv0, common);
-       exits("usage");
+       threadexits("usage");
 }
 
 void
@@ -1332,10 +1378,12 @@ threadmain(int argc, char **argv)
        case 'a': mflag |= MAFTER; break;
        case 'b': mflag |= MBEFORE; break;
        case 'm': mtpt = EARGF(usage()); break;
+       case 'M': mtpt = nil; break;
        case 'u': uidfile = EARGF(usage()); break;
        case 'U': uidfile = nil; break;
        case 'g': gidfile = EARGF(usage()); break;
        case 'G': gidfile = nil; break;
+       case 'r': root = EARGF(usage()); break;
        default: usage();
        }ARGEND;
        
@@ -1363,7 +1411,7 @@ threadmain(int argc, char **argv)
                }
                pipe(pfd);
                rdfd = wrfd = pfd[0];
-               procrfork(startssh, nil, mainstacksize, RFFDG|RFNOTEG);
+               procrfork(startssh, nil, mainstacksize, RFFDG|RFNOTEG|RFNAMEG);
                close(pfd[1]);
        }
 
@@ -1374,7 +1422,7 @@ threadmain(int argc, char **argv)
        passwdparse(uidtab, readfile(uidfile));
        passwdparse(gidtab, readfile(gidfile));
        
-       procrfork(sendproc, 0, mainstacksize, RFNOTEG);
-       procrfork(recvproc, 0, mainstacksize, RFNOTEG);
        threadpostmountsrv(&sshfssrv, svc, mtpt, MCREATE | mflag);
+
+       threadexits(nil);
 }