int readonly;
int debug;
+char *root = ".";
#define dprint(...) if(debug) fprint(2, __VA_ARGS__)
#pragma varargck type "Σ" int
uchar *hand;
int handn;
Qid qid;
+ int dirreads;
Dir *dirent;
- int ndirent;
+ int ndirent, dirpos;
uchar direof;
};
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);
}
}
void
-putsfid(SFid *s)
+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++)
+ freedir1(&s->dirent[i]);
+ free(s->dirent);
+ s->dirent = nil;
+ s->ndirent = 0;
+ s->dirpos = 0;
+}
+
+
+void
+putsfid(SFid *s)
+{
if(s == nil) return;
free(s->fn);
free(s->hand);
- for(i = 0; i < s->ndirent; i++){
- d = &s->dirent[i];
- free(d->name);
- free(d->uid);
- free(d->gid);
- free(d->muid);
- }
- free(s->dirent);
+ freedir(s);
free(s);
}
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;
- return q;
+ return pathcat(p, "..");
}
char *
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
}
void
-walkprocess(Req *r, int isdir, char *e)
+walkprocess(Req *r, char *e)
{
char *p;
SFid *sf;
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;
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;
- if((perm & 0040000) != 0) d->mode |= DMDIR;
+ if((perm & 0170000) == 0040000) d->mode |= DMDIR;
}
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;
uchar *r, *p, *e;
u32int fl;
int uid, gid;
-
+
werrstr("phase error");
r = emalloc9p(MAXATTRIB);
e = r + MAXATTRIB;
return p - r;
}
+int
+attrfixupqid(Qid *qid)
+{
+ u32int flags;
+ uchar *p;
+
+ if(unpack(rxpkt, rxlen, "_____u", &flags) < 0) return -1;
+ p = rxpkt + 9;
+ 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
parsedir(SFid *sf)
{
if(unpack(rxpkt, rxlen, "_____u", &c) < 0) return -1;
wlock(sf);
- sf->dirent = erealloc9p(sf->dirent, (sf->ndirent + c) * sizeof(Dir));
- d = sf->dirent + sf->ndirent;
+ freedir(sf);
+ sf->dirent = emalloc9p(c * sizeof(Dir));
+ d = sf->dirent;
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);
wunlock(sf);
return 0;
err:
+ freedir1(d);
wunlock(sf);
return -1;
}
+
void
readprocess(Req *r)
{
- int start;
+ int i;
uchar *p, *ep;
uint rv;
SFid *sf;
-
- if((r->fid->qid.type & QTDIR) == 0){
- submitreq(r);
+
+ sf = r->fid->aux;
+ wlock(sf);
+ if(sf->direof){
+ wunlock(sf);
+ respond(r, nil);
return;
}
-
- if(r->ifcall.offset == 0)
- start = 0;
- else
- start = r->fid->dirindex;
-
- p = (uchar*)r->ofcall.data;
- ep = p+r->ifcall.count;
- sf = r->fid->aux;
-
- rlock(sf);
+ i = sf->dirpos;
+ p = (uchar*)r->ofcall.data + r->ofcall.count;
+ ep = (uchar*)r->ofcall.data + r->ifcall.count;
+ rv = ep - p;
while(p < ep){
- if(start >= sf->ndirent)
- if(sf->direof)
- break;
- else{
- runlock(sf);
- submitreq(r);
- return;
- }
- rv = convD2M(&sf->dirent[start], p, ep-p);
+ if(i >= sf->ndirent)
+ break;
+ rv = convD2M(&sf->dirent[i], p, ep-p);
if(rv <= BIT16SZ)
break;
p += rv;
- start++;
+ i++;
}
- runlock(sf);
- r->fid->dirindex = start;
+ sf->dirpos = i;
+ if(i >= sf->ndirent)
+ freedir(sf);
+ wunlock(sf);
r->ofcall.count = p - (uchar*)r->ofcall.data;
- respond(r, nil);
+ if(rv <= BIT16SZ)
+ respond(r, nil);
+ else
+ submitreq(r);
}
void
-sshfsattach(Req *r)
+sshfsread(Req *r)
{
SFid *sf;
- if(r->ifcall.aname != nil && *r->ifcall.aname != 0 && r->aux == nil){
+ if((r->fid->qid.type & QTDIR) == 0){
submitreq(r);
return;
}
- 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);
+ sf = r->fid->aux;
+ if(r->ifcall.offset == 0){
+ wlock(sf);
+ freedir(sf);
+ if(sf->dirreads > 0){
+ r->aux = (void*)-1;
+ submitreq(r);
+ wunlock(sf);
+ return;
+ }
+ wunlock(sf);
+ }
+ readprocess(r);
+}
+
+void
+sshfsattach(Req *r)
+{
+ SFid *sf;
+
+ 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);
+ }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);
+ }
}
void
sf = r->req->fid != nil ? r->req->fid->aux : nil;
switch(r->req->ifcall.type){
case Tattach:
- sendpkt("bus", SSH_FXP_STAT, r->reqid, r->req->ifcall.aname, strlen(r->req->ifcall.aname));
+ sendpkt("bus", SSH_FXP_STAT, r->reqid, sf->fn, strlen(sf->fn));
break;
case Twalk:
sendpkt("bus", SSH_FXP_STAT, r->reqid, r->req->aux, strlen(r->req->aux));
free(s);
break;
case Tread:
- rlock(sf);
- if((r->req->fid->qid.type & QTDIR) != 0)
- sendpkt("bus", SSH_FXP_READDIR, r->reqid, sf->hand, sf->handn);
- else
+ 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);
+ 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));
+ }else{
+ sf->dirreads++;
+ sendpkt("bus", SSH_FXP_READDIR, r->reqid, sf->hand, sf->handn);
+ }
+ wunlock(sf);
+ }else{
+ rlock(sf);
sendpkt("busvuu", SSH_FXP_READ, r->reqid, sf->hand, sf->handn,
r->req->ifcall.offset, r->req->ifcall.count);
- runlock(sf);
+ runlock(sf);
+ }
break;
case Twrite:
x = r->req->ifcall.count - r->req->ofcall.count;
rlock(sf);
r->req->d.name = finalelem(sf->fn);
r->req->d.qid = sf->qid;
- if(sf->handn > 0)
+ if(sf->handn > 0 && (sf->qid.type & QTDIR) == 0)
sendpkt("bus", SSH_FXP_FSTAT, r->reqid, sf->hand, sf->handn);
else
sendpkt("bus", SSH_FXP_STAT, r->reqid, sf->fn, strlen(sf->fn));
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("buss", SSH_FXP_RENAME, r->reqid, sf->fn, strlen(sf->fn), t, strlen(t));
runlock(sf);
break;
}
else
sendpkt("bus[", SSH_FXP_SETSTAT, r->reqid, sf->fn, strlen(sf->fn), s, x);
runlock(sf);
+ free(s);
break;
case Tremove:
rlock(sf);
SFid *sf;
int t, id;
u32int code;
- char *msg, *lang, *hand;
+ char *msg, *lang, *hand, *s;
int msgn, langn, handn;
- uchar *p;
+ int okresp;
char *e;
threadsetname("recv");
}
id = GET4(rxpkt + 1);
if(id >= MAXREQID){
- fprint(2, "sshfs: received response with id out of range, %d > %d\n", id, MAXREQID);
+ fprint(2, "sshfs: received %Σ response with id out of range, %d > %d\n", t, id, MAXREQID);
continue;
}
qlock(&sreqidlock);
r = sreqrd[id];
if(r != nil){
sreqrd[id] = nil;
+ r->reqid = -1;
rwakeup(&sreqidrend);
}
qunlock(&sreqidlock);
if(r == nil){
- fprint(2, "sshfs: received response to non-existent request (req id = %d)\n", id);
+ fprint(2, "sshfs: received %Σ response to non-existent request (req id = %d)\n", t, id);
continue;
}
if(r->closefid != nil){
sysfatal("recvproc: r->req == nil");
sf = r->req->fid != nil ? r->req->fid->aux : nil;
+ 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;
- if(unpack(rxpkt, rxlen, "_____u", &code) < 0) goto garbage;
+ if(attrfixupqid(&r->req->ofcall.qid) < 0)
+ goto garbage;
r->req->aux = (void*)-1;
- if((code & 4) == 0){
- fprint(2, "sshfs: can't determine if %s is a directory\n", r->req->ifcall.aname);
- sshfsattach(r->req);
- break;
- }
- p = rxpkt + 9;
- if(code & 1) p += 8;
- if(code & 2) p += 8;
- if(p + 4 > rxpkt + rxlen) goto garbage;
- if((GET4(p) & 0040000) == 0)
+ 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;
- if(unpack(rxpkt, rxlen, "_____u", &code) < 0) goto garbage;
- if((code & 4) == 0){
- fprint(2, "sshfs: can't determine if %s is a directory\n", ((SFid*)r->req->fid)->fn);
- walkprocess(r->req, 0, nil);
- break;
- }
- p = rxpkt + 9;
- if(code & 1) p += 8;
- if(code & 2) p += 8;
- if(p + 4 > rxpkt + rxlen) goto garbage;
- walkprocess(r->req, GET4(p) & 0040000, 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(t == SSH_FXP_STATUS && r->req->aux == (void*)-1){
- if(unpack(rxpkt, rxlen, "_____u", &code) < 0) goto garbage;
- if(code != SSH_FX_OK) goto common;
+ if(okresp && r->req->aux == (void*)-1){
submitreq(r->req);
break;
}
/* wet floor */
- case Topen:
+ case Topen: opendir:
if(t != SSH_FXP_HANDLE) goto common;
if(unpack(rxpkt, rxlen, "_____s", &hand, &handn) < 0) goto garbage;
wlock(sf);
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);
- respond(r->req, nil);
+ if(r->req->ifcall.type == Tread){
+ r->req->aux = nil;
+ readprocess(r->req);
+ }else
+ respond(r->req, nil);
break;
case Tread:
if((r->req->fid->qid.type & QTDIR) != 0){
- if(t != SSH_FXP_NAME) goto common;
- if(parsedir(sf) < 0) goto garbage;
- readprocess(r->req);
+ if(r->req->aux == (void*)-1){
+ if(t != SSH_FXP_STATUS) goto common;
+ /* reopen even if close failed */
+ r->req->aux = (void*)-2;
+ submitreq(r->req);
+ }else if(r->req->aux == (void*)-2)
+ goto opendir;
+ else{
+ if(t != SSH_FXP_NAME) goto common;
+ if(parsedir(sf) < 0) goto garbage;
+ readprocess(r->req);
+ }
break;
}
if(t != SSH_FXP_DATA) goto common;
break;
case Twrite:
if(t != SSH_FXP_STATUS) goto common;
- if(unpack(rxpkt, rxlen, "_____u", &code) < 0) goto garbage;
- if(code == SSH_FX_OK){
+ if(okresp){
r->req->ofcall.count += r->req->ofcall.offset;
if(r->req->ofcall.count == r->req->ifcall.count)
respond(r->req, nil);
respond(r->req, nil);
break;
case Twstat:
- if(t != SSH_FXP_STATUS || r->req->d.name == nil || *r->req->d.name == 0) goto common;
- if(unpack(rxpkt, rxlen, "_____u", &code) < 0) goto garbage;
- if(code != SSH_FX_OK) goto common;
+ 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;
submitreq(r->req);
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);
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
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};
free(r->aux);
}
+void
+sshfsstart(Srv *)
+{
+ proccreate(sendproc, nil, mainstacksize);
+ proccreate(recvproc, nil, mainstacksize);
+}
+
void
sshfsend(Srv *)
{
}
Srv sshfssrv = {
+ .start sshfsstart,
.attach sshfsattach,
.walk sshfswalk,
.open submitreq,
.create submitreq,
- .read readprocess,
+ .read sshfsread,
.write submitreq,
.stat submitreq,
.wstat submitreq,
.remove submitreq,
.destroyfid sshfsdestroyfid,
.destroyreq sshfsdestroyreq,
- .end sshfsend
+ .end sshfsend,
};
char *
switch(recvpkt()){
case SSH_FXP_STATUS:
if(unpack(rxpkt, rxlen, "_____u", &code) < 0) goto err;
- print("%d\n", code);
if(code == SSH_FX_EOF) goto out;
default:
goto err;
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;
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;
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");
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;
}
pipe(pfd);
rdfd = wrfd = pfd[0];
- procrfork(startssh, nil, mainstacksize, RFFDG|RFNOTEG);
+ procrfork(startssh, nil, mainstacksize, RFFDG|RFNOTEG|RFNAMEG);
close(pfd[1]);
}
passwdparse(uidtab, readfile(uidfile));
passwdparse(gidtab, readfile(gidfile));
- procrfork(sendproc, 0, mainstacksize, RFNOTEG);
- procrfork(recvproc, 0, mainstacksize, RFNOTEG);
threadpostmountsrv(&sshfssrv, svc, mtpt, MCREATE | mflag);
+
+ exits(nil);
}