2017-04-28 15:41:48 +00:00
|
|
|
#include <u.h>
|
|
|
|
#include <libc.h>
|
|
|
|
#include <fcall.h>
|
|
|
|
#include <thread.h>
|
|
|
|
#include <9p.h>
|
|
|
|
#include <libsec.h>
|
|
|
|
|
|
|
|
int readonly;
|
|
|
|
int debug;
|
2017-04-29 18:44:01 +00:00
|
|
|
char *root = ".";
|
2017-04-28 15:41:48 +00:00
|
|
|
#define dprint(...) if(debug) fprint(2, __VA_ARGS__)
|
|
|
|
#pragma varargck type "Σ" int
|
|
|
|
|
|
|
|
enum {
|
|
|
|
MAXPACK = 34000,
|
|
|
|
MAXWRITE = 32768,
|
|
|
|
MAXATTRIB = 64,
|
|
|
|
VERSION = 3,
|
2017-04-28 16:34:24 +00:00
|
|
|
MAXREQID = 32,
|
|
|
|
HASH = 64
|
2017-04-28 15:41:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
SSH_FXP_INIT = 1,
|
|
|
|
SSH_FXP_VERSION = 2,
|
|
|
|
SSH_FXP_OPEN = 3,
|
|
|
|
SSH_FXP_CLOSE = 4,
|
|
|
|
SSH_FXP_READ = 5,
|
|
|
|
SSH_FXP_WRITE = 6,
|
|
|
|
SSH_FXP_LSTAT = 7,
|
|
|
|
SSH_FXP_FSTAT = 8,
|
|
|
|
SSH_FXP_SETSTAT = 9,
|
|
|
|
SSH_FXP_FSETSTAT = 10,
|
|
|
|
SSH_FXP_OPENDIR = 11,
|
|
|
|
SSH_FXP_READDIR = 12,
|
|
|
|
SSH_FXP_REMOVE = 13,
|
|
|
|
SSH_FXP_MKDIR = 14,
|
|
|
|
SSH_FXP_RMDIR = 15,
|
|
|
|
SSH_FXP_REALPATH = 16,
|
|
|
|
SSH_FXP_STAT = 17,
|
|
|
|
SSH_FXP_RENAME = 18,
|
|
|
|
SSH_FXP_READLINK = 19,
|
|
|
|
SSH_FXP_SYMLINK = 20,
|
|
|
|
SSH_FXP_STATUS = 101,
|
|
|
|
SSH_FXP_HANDLE = 102,
|
|
|
|
SSH_FXP_DATA = 103,
|
|
|
|
SSH_FXP_NAME = 104,
|
|
|
|
SSH_FXP_ATTRS = 105,
|
|
|
|
SSH_FXP_EXTENDED = 200,
|
|
|
|
SSH_FXP_EXTENDED_REPLY = 201,
|
|
|
|
|
|
|
|
SSH_FXF_READ = 0x00000001,
|
|
|
|
SSH_FXF_WRITE = 0x00000002,
|
|
|
|
SSH_FXF_APPEND = 0x00000004,
|
|
|
|
SSH_FXF_CREAT = 0x00000008,
|
|
|
|
SSH_FXF_TRUNC = 0x00000010,
|
|
|
|
SSH_FXF_EXCL = 0x00000020,
|
|
|
|
SSH_FILEXFER_ATTR_SIZE = 0x00000001,
|
|
|
|
SSH_FILEXFER_ATTR_UIDGID = 0x00000002,
|
|
|
|
SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004,
|
|
|
|
SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008,
|
|
|
|
SSH_FILEXFER_ATTR_EXTENDED = 0x80000000,
|
|
|
|
|
|
|
|
SSH_FX_OK = 0,
|
|
|
|
SSH_FX_EOF = 1,
|
|
|
|
SSH_FX_NO_SUCH_FILE = 2,
|
|
|
|
SSH_FX_PERMISSION_DENIED = 3,
|
|
|
|
SSH_FX_FAILURE = 4,
|
|
|
|
SSH_FX_BAD_MESSAGE = 5,
|
|
|
|
SSH_FX_NO_CONNECTION = 6,
|
|
|
|
SSH_FX_CONNECTION_LOST = 7,
|
|
|
|
SSH_FX_OP_UNSUPPORTED = 8,
|
|
|
|
};
|
|
|
|
|
|
|
|
char *errors[] = {
|
|
|
|
[SSH_FX_OK] "success",
|
|
|
|
[SSH_FX_EOF] "end of file",
|
|
|
|
[SSH_FX_NO_SUCH_FILE] "file does not exist",
|
|
|
|
[SSH_FX_PERMISSION_DENIED] "permission denied",
|
|
|
|
[SSH_FX_FAILURE] "failure",
|
|
|
|
[SSH_FX_BAD_MESSAGE] "bad message",
|
|
|
|
[SSH_FX_NO_CONNECTION] "no connection",
|
|
|
|
[SSH_FX_CONNECTION_LOST] "connection lost",
|
|
|
|
[SSH_FX_OP_UNSUPPORTED] "unsupported operation",
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct SFid SFid;
|
|
|
|
typedef struct SReq SReq;
|
2017-04-28 16:34:24 +00:00
|
|
|
typedef struct IDEnt IDEnt;
|
2017-04-28 15:41:48 +00:00
|
|
|
|
|
|
|
struct SFid {
|
|
|
|
RWLock;
|
|
|
|
char *fn;
|
|
|
|
uchar *hand;
|
|
|
|
int handn;
|
|
|
|
Qid qid;
|
2017-04-28 18:21:03 +00:00
|
|
|
int dirreads;
|
2017-04-28 15:41:48 +00:00
|
|
|
Dir *dirent;
|
2017-04-28 18:21:03 +00:00
|
|
|
int ndirent, dirpos;
|
2017-04-28 15:41:48 +00:00
|
|
|
uchar direof;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SReq {
|
|
|
|
Req *req;
|
|
|
|
SFid *closefid;
|
|
|
|
int reqid;
|
|
|
|
SReq *next;
|
|
|
|
};
|
|
|
|
|
2017-04-28 16:34:24 +00:00
|
|
|
struct IDEnt {
|
|
|
|
char *name;
|
|
|
|
int id;
|
|
|
|
IDEnt *next;
|
|
|
|
};
|
|
|
|
IDEnt *uidtab[HASH], *gidtab[HASH];
|
|
|
|
|
2017-04-28 15:41:48 +00:00
|
|
|
int rdfd, wrfd;
|
|
|
|
SReq *sreqrd[MAXREQID];
|
|
|
|
QLock sreqidlock;
|
|
|
|
Rendez sreqidrend = {.l = &sreqidlock};
|
|
|
|
|
|
|
|
SReq *sreqwr, **sreqlast = &sreqwr;
|
|
|
|
QLock sreqwrlock;
|
|
|
|
Rendez writerend = {.l = &sreqwrlock};
|
|
|
|
|
|
|
|
#define PUT4(p, u) (p)[0] = (u)>>24, (p)[1] = (u)>>16, (p)[2] = (u)>>8, (p)[3] = (u)
|
|
|
|
#define GET4(p) ((u32int)(p)[3] | (u32int)(p)[2]<<8 | (u32int)(p)[1]<<16 | (u32int)(p)[0]<<24)
|
|
|
|
|
|
|
|
int
|
|
|
|
fxpfmt(Fmt *f)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
n = va_arg(f->args, int);
|
|
|
|
switch(n){
|
|
|
|
case SSH_FXP_INIT: fmtstrcpy(f, "SSH_FXP_INIT"); break;
|
|
|
|
case SSH_FXP_VERSION: fmtstrcpy(f, "SSH_FXP_VERSION"); break;
|
|
|
|
case SSH_FXP_OPEN: fmtstrcpy(f, "SSH_FXP_OPEN"); break;
|
|
|
|
case SSH_FXP_CLOSE: fmtstrcpy(f, "SSH_FXP_CLOSE"); break;
|
|
|
|
case SSH_FXP_READ: fmtstrcpy(f, "SSH_FXP_READ"); break;
|
|
|
|
case SSH_FXP_WRITE: fmtstrcpy(f, "SSH_FXP_WRITE"); break;
|
|
|
|
case SSH_FXP_LSTAT: fmtstrcpy(f, "SSH_FXP_LSTAT"); break;
|
|
|
|
case SSH_FXP_FSTAT: fmtstrcpy(f, "SSH_FXP_FSTAT"); break;
|
|
|
|
case SSH_FXP_SETSTAT: fmtstrcpy(f, "SSH_FXP_SETSTAT"); break;
|
|
|
|
case SSH_FXP_FSETSTAT: fmtstrcpy(f, "SSH_FXP_FSETSTAT"); break;
|
|
|
|
case SSH_FXP_OPENDIR: fmtstrcpy(f, "SSH_FXP_OPENDIR"); break;
|
|
|
|
case SSH_FXP_READDIR: fmtstrcpy(f, "SSH_FXP_READDIR"); break;
|
|
|
|
case SSH_FXP_REMOVE: fmtstrcpy(f, "SSH_FXP_REMOVE"); break;
|
|
|
|
case SSH_FXP_MKDIR: fmtstrcpy(f, "SSH_FXP_MKDIR"); break;
|
|
|
|
case SSH_FXP_RMDIR: fmtstrcpy(f, "SSH_FXP_RMDIR"); break;
|
|
|
|
case SSH_FXP_REALPATH: fmtstrcpy(f, "SSH_FXP_REALPATH"); break;
|
|
|
|
case SSH_FXP_STAT: fmtstrcpy(f, "SSH_FXP_STAT"); break;
|
|
|
|
case SSH_FXP_RENAME: fmtstrcpy(f, "SSH_FXP_RENAME"); break;
|
|
|
|
case SSH_FXP_READLINK: fmtstrcpy(f, "SSH_FXP_READLINK"); break;
|
|
|
|
case SSH_FXP_SYMLINK: fmtstrcpy(f, "SSH_FXP_SYMLINK"); break;
|
|
|
|
case SSH_FXP_STATUS: fmtstrcpy(f, "SSH_FXP_STATUS"); break;
|
|
|
|
case SSH_FXP_HANDLE: fmtstrcpy(f, "SSH_FXP_HANDLE"); break;
|
|
|
|
case SSH_FXP_DATA: fmtstrcpy(f, "SSH_FXP_DATA"); break;
|
|
|
|
case SSH_FXP_NAME: fmtstrcpy(f, "SSH_FXP_NAME"); break;
|
|
|
|
case SSH_FXP_ATTRS: fmtstrcpy(f, "SSH_FXP_ATTRS"); break;
|
|
|
|
case SSH_FXP_EXTENDED: fmtstrcpy(f, "SSH_FXP_EXTENDED"); break;
|
|
|
|
case SSH_FXP_EXTENDED_REPLY: fmtstrcpy(f, "SSH_FXP_EXTENDED_REPLY");
|
|
|
|
default: fmtprint(f, "%d", n);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-28 16:34:24 +00:00
|
|
|
char *
|
|
|
|
idlookup(IDEnt **tab, int id)
|
|
|
|
{
|
|
|
|
IDEnt *p;
|
|
|
|
|
|
|
|
for(p = tab[(ulong)id % HASH]; p != nil; p = p->next)
|
|
|
|
if(p->id == id)
|
2017-04-30 16:28:06 +00:00
|
|
|
return estrdup9p(p->name);
|
2017-04-28 16:34:24 +00:00
|
|
|
return smprint("%d", id);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
namelookup(IDEnt **tab, char *name)
|
|
|
|
{
|
|
|
|
IDEnt *p;
|
|
|
|
int i;
|
|
|
|
char *q;
|
|
|
|
|
|
|
|
for(i = 0; i < HASH; i++)
|
|
|
|
for(p = tab[i]; p != nil; p = p->next)
|
|
|
|
if(strcmp(p->name, name) == 0)
|
|
|
|
return p->id;
|
|
|
|
i = strtol(name, &q, 10);
|
|
|
|
if(*q == 0) return i;
|
|
|
|
werrstr("unknown %s '%s'", tab == uidtab ? "user" : "group", name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-04-28 15:41:48 +00:00
|
|
|
int
|
|
|
|
vpack(uchar *p, int n, char *fmt, va_list a)
|
|
|
|
{
|
|
|
|
uchar *p0 = p, *e = p+n;
|
|
|
|
u32int u;
|
|
|
|
u64int v;
|
|
|
|
void *s;
|
|
|
|
int c;
|
|
|
|
|
|
|
|
for(;;){
|
|
|
|
switch(c = *fmt++){
|
|
|
|
case '\0':
|
|
|
|
return p - p0;
|
|
|
|
case '_':
|
|
|
|
if(++p > e) goto err;
|
|
|
|
break;
|
|
|
|
case '.':
|
|
|
|
*va_arg(a, void**) = p;
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
if(p >= e) goto err;
|
|
|
|
*p++ = va_arg(a, int);
|
|
|
|
break;
|
|
|
|
case '[':
|
|
|
|
case 's':
|
|
|
|
s = va_arg(a, void*);
|
|
|
|
u = va_arg(a, int);
|
|
|
|
if(c == 's'){
|
|
|
|
if(p+4 > e) goto err;
|
|
|
|
PUT4(p, u), p += 4;
|
|
|
|
}
|
|
|
|
if(u > e-p) goto err;
|
|
|
|
memmove(p, s, u);
|
|
|
|
p += u;
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
u = va_arg(a, int);
|
|
|
|
if(p+4 > e) goto err;
|
|
|
|
PUT4(p, u), p += 4;
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
v = va_arg(a, vlong);
|
|
|
|
if(p+8 > e) goto err;
|
|
|
|
u = v>>32; PUT4(p, u), p += 4;
|
|
|
|
u = v; PUT4(p, u), p += 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
vunpack(uchar *p, int n, char *fmt, va_list a)
|
|
|
|
{
|
|
|
|
uchar *p0 = p, *e = p+n;
|
|
|
|
u32int u;
|
|
|
|
u64int v;
|
|
|
|
void *s;
|
|
|
|
|
|
|
|
for(;;){
|
|
|
|
switch(*fmt++){
|
|
|
|
case '\0':
|
|
|
|
return p - p0;
|
|
|
|
case '_':
|
|
|
|
if(++p > e) goto err;
|
|
|
|
break;
|
|
|
|
case '.':
|
|
|
|
*va_arg(a, void**) = p;
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
if(p >= e) goto err;
|
|
|
|
*va_arg(a, int*) = *p++;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
if(p+4 > e) goto err;
|
|
|
|
u = GET4(p), p += 4;
|
|
|
|
if(u > e-p) goto err;
|
|
|
|
*va_arg(a, void**) = p;
|
|
|
|
*va_arg(a, int*) = u;
|
|
|
|
p += u;
|
|
|
|
break;
|
|
|
|
case '[':
|
|
|
|
s = va_arg(a, void*);
|
|
|
|
u = va_arg(a, int);
|
|
|
|
if(u > e-p) goto err;
|
|
|
|
memmove(s, p, u);
|
|
|
|
p += u;
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
if(p+4 > e) goto err;
|
|
|
|
u = GET4(p);
|
|
|
|
*va_arg(a, int*) = u;
|
|
|
|
p += 4;
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
if(p+8 > e) goto err;
|
|
|
|
v = (u64int)GET4(p) << 32;
|
|
|
|
v |= (u32int)GET4(p+4);
|
|
|
|
*va_arg(a, vlong*) = v;
|
|
|
|
p += 8;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
pack(uchar *p, int n, char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list a;
|
|
|
|
va_start(a, fmt);
|
|
|
|
n = vpack(p, n, fmt, a);
|
|
|
|
va_end(a);
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
int
|
|
|
|
unpack(uchar *p, int n, char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list a;
|
|
|
|
va_start(a, fmt);
|
|
|
|
n = vunpack(p, n, fmt, a);
|
|
|
|
va_end(a);
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sendpkt(char *fmt, ...)
|
|
|
|
{
|
|
|
|
static uchar buf[MAXPACK];
|
|
|
|
int n;
|
|
|
|
va_list a;
|
|
|
|
|
|
|
|
va_start(a, fmt);
|
|
|
|
n = vpack(buf+4, sizeof(buf)-4, fmt, a);
|
|
|
|
va_end(a);
|
|
|
|
if(n < 0) {
|
|
|
|
sysfatal("sendpkt: message too big");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
PUT4(buf, n);
|
|
|
|
n += 4;
|
|
|
|
|
|
|
|
dprint("SFTP --> %Σ\n", (int)buf[4]);
|
|
|
|
if(write(wrfd, buf, n) != n)
|
|
|
|
sysfatal("write: %r");
|
|
|
|
}
|
|
|
|
|
|
|
|
static uchar rxpkt[MAXPACK];
|
|
|
|
static int rxlen;
|
|
|
|
|
|
|
|
int
|
|
|
|
recvpkt(void)
|
|
|
|
{
|
|
|
|
static uchar rxbuf[MAXPACK];
|
|
|
|
static int rxfill;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
while(rxfill < 4 || rxfill < (rxlen = GET4(rxbuf) + 4) && rxlen <= MAXPACK){
|
|
|
|
rc = read(rdfd, rxbuf + rxfill, MAXPACK - rxfill);
|
|
|
|
if(rc < 0) sysfatal("read: %r");
|
|
|
|
if(rc == 0) sysfatal("read: eof");
|
|
|
|
rxfill += rc;
|
|
|
|
}
|
|
|
|
if(rxlen > MAXPACK) sysfatal("received garbage");
|
|
|
|
memmove(rxpkt, rxbuf + 4, rxlen - 4);
|
|
|
|
memmove(rxbuf, rxbuf + rxlen, rxfill - rxlen);
|
|
|
|
rxfill -= rxlen;
|
|
|
|
rxlen -= 4;
|
|
|
|
dprint("SFTP <-- %Σ\n", (int)rxpkt[0]);
|
|
|
|
return rxpkt[0];
|
|
|
|
}
|
|
|
|
|
2017-04-30 16:28:06 +00:00
|
|
|
void
|
|
|
|
freedir1(Dir *d)
|
|
|
|
{
|
|
|
|
free(d->name);
|
|
|
|
free(d->uid);
|
|
|
|
free(d->gid);
|
|
|
|
free(d->muid);
|
|
|
|
}
|
|
|
|
|
2017-04-28 15:41:48 +00:00
|
|
|
void
|
2017-04-28 18:21:03 +00:00
|
|
|
freedir(SFid *s)
|
2017-04-28 15:41:48 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2017-04-30 16:28:06 +00:00
|
|
|
for(i = 0; i < s->ndirent; i++)
|
|
|
|
freedir1(&s->dirent[i]);
|
2017-04-28 15:41:48 +00:00
|
|
|
free(s->dirent);
|
2017-04-28 18:21:03 +00:00
|
|
|
s->dirent = nil;
|
|
|
|
s->ndirent = 0;
|
|
|
|
s->dirpos = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
putsfid(SFid *s)
|
|
|
|
{
|
|
|
|
if(s == nil) return;
|
|
|
|
free(s->fn);
|
|
|
|
free(s->hand);
|
|
|
|
freedir(s);
|
2017-04-28 15:41:48 +00:00
|
|
|
free(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);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
submitsreq(SReq *s)
|
|
|
|
{
|
|
|
|
qlock(&sreqwrlock);
|
|
|
|
*sreqlast = s;
|
|
|
|
sreqlast = &s->next;
|
|
|
|
rwakeup(&writerend);
|
|
|
|
qunlock(&sreqwrlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
submitreq(Req *r)
|
|
|
|
{
|
|
|
|
SReq *s;
|
|
|
|
|
|
|
|
s = emalloc9p(sizeof(SReq));
|
|
|
|
s->reqid = -1;
|
|
|
|
s->req = r;
|
|
|
|
submitsreq(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
pathcat(char *p, char *c)
|
|
|
|
{
|
2017-04-30 16:28:06 +00:00
|
|
|
return cleanname(smprint("%s/%s", p, c));
|
2017-04-28 15:41:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
parentdir(char *p)
|
|
|
|
{
|
2017-04-30 16:28:06 +00:00
|
|
|
return pathcat(p, "..");
|
2017-04-28 15:41:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
finalelem(char *p)
|
|
|
|
{
|
|
|
|
char *q;
|
|
|
|
|
|
|
|
q = strrchr(p, '/');
|
2017-04-30 16:28:06 +00:00
|
|
|
if(q == nil) return estrdup9p(p);
|
|
|
|
return estrdup9p(q+1);
|
2017-04-28 15:41:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
u64int
|
|
|
|
qidcalc(char *c)
|
|
|
|
{
|
|
|
|
uchar dig[SHA1dlen];
|
|
|
|
|
|
|
|
sha1((uchar *) c, strlen(c), dig, nil);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-09-22 09:48:41 +00:00
|
|
|
walkprocess(Req *r, char *e)
|
2017-04-28 15:41:48 +00:00
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
SFid *sf;
|
|
|
|
|
|
|
|
sf = r->newfid->aux;
|
|
|
|
if(e != nil){
|
|
|
|
r->ofcall.nwqid--;
|
|
|
|
if(r->ofcall.nwqid == 0){
|
|
|
|
respond(r, e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
p = r->aux;
|
|
|
|
r->aux = parentdir(p);
|
|
|
|
free(p);
|
|
|
|
submitreq(r);
|
|
|
|
}else{
|
|
|
|
assert(r->ofcall.nwqid > 0);
|
|
|
|
wlock(sf);
|
|
|
|
free(sf->fn);
|
|
|
|
sf->fn = r->aux;
|
|
|
|
r->aux = nil;
|
|
|
|
sf->qid = r->ofcall.wqid[r->ofcall.nwqid - 1];
|
|
|
|
wunlock(sf);
|
|
|
|
respond(r, nil);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
attrib2dir(uchar *p0, uchar *ep, Dir *d)
|
|
|
|
{
|
|
|
|
uchar *p;
|
|
|
|
int i, rc, extn, extvn;
|
|
|
|
u32int flags, uid, gid, perm, next;
|
|
|
|
uchar *exts, *extvs;
|
|
|
|
|
|
|
|
p = p0;
|
|
|
|
if(p + 4 > ep) return -1;
|
|
|
|
flags = GET4(p), p += 4;
|
|
|
|
if((flags & SSH_FILEXFER_ATTR_SIZE) != 0){
|
|
|
|
rc = unpack(p, ep - p, "v", &d->length); if(rc < 0) return -1; p += rc;
|
|
|
|
}
|
|
|
|
if((flags & SSH_FILEXFER_ATTR_UIDGID) != 0){
|
|
|
|
rc = unpack(p, ep - p, "uu", &uid, &gid); if(rc < 0) return -1; p += rc;
|
2017-04-28 16:34:24 +00:00
|
|
|
d->uid = idlookup(uidtab, uid);
|
|
|
|
d->gid = idlookup(gidtab, gid);
|
2017-04-28 15:41:48 +00:00
|
|
|
}else{
|
2017-04-30 16:28:06 +00:00
|
|
|
d->uid = estrdup9p("sshfs");
|
|
|
|
d->gid = estrdup9p("sshfs");
|
2017-04-28 15:41:48 +00:00
|
|
|
}
|
2017-04-30 16:28:06 +00:00
|
|
|
d->muid = estrdup9p(d->uid);
|
2017-04-28 15:41:48 +00:00
|
|
|
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;
|
2017-04-28 20:42:38 +00:00
|
|
|
if((perm & 0170000) == 0040000) d->mode |= DMDIR;
|
2017-04-28 15:41:48 +00:00
|
|
|
}
|
|
|
|
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;
|
2017-09-22 09:48:41 +00:00
|
|
|
d->qid.vers = d->mtime;
|
2017-04-28 15:41:48 +00:00
|
|
|
}
|
|
|
|
if((flags & SSH_FILEXFER_ATTR_EXTENDED) != 0){
|
|
|
|
rc = unpack(p, ep - p, "u", &next); if(rc < 0) return -1; p += rc;
|
|
|
|
for(i = 0; i < next; i++){
|
|
|
|
rc = unpack(p, ep - p, "ss", &exts, &extn, &extvs, &extvn); if(rc < 0) return -1; p += rc;
|
|
|
|
exts[extn] = extvs[extvn] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return p - p0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
dir2attrib(Dir *d, uchar **rp)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
uchar *r, *p, *e;
|
|
|
|
u32int fl;
|
|
|
|
int uid, gid;
|
2017-09-22 09:48:41 +00:00
|
|
|
|
2017-04-28 15:41:48 +00:00
|
|
|
werrstr("phase error");
|
|
|
|
r = emalloc9p(MAXATTRIB);
|
|
|
|
e = r + MAXATTRIB;
|
|
|
|
fl = 0;
|
|
|
|
p = r + 4;
|
|
|
|
if(d->length != (uvlong)-1){
|
|
|
|
fl |= SSH_FILEXFER_ATTR_SIZE;
|
|
|
|
rc = pack(p, e - p, "v", d->length); if(rc < 0) return -1; p += rc;
|
|
|
|
}
|
|
|
|
if(d->uid != nil && *d->uid != 0 || d->gid != nil && *d->gid != 0){
|
|
|
|
/* FIXME: sending -1 for "don't change" works with openssh, but violates the spec */
|
|
|
|
if(d->uid != nil && *d->uid != 0){
|
2017-04-28 16:34:24 +00:00
|
|
|
uid = namelookup(uidtab, d->uid);
|
|
|
|
if(uid == -1)
|
2017-04-28 15:41:48 +00:00
|
|
|
return -1;
|
|
|
|
}else
|
|
|
|
uid = -1;
|
|
|
|
if(d->gid != nil && *d->gid != 0){
|
2017-04-28 16:34:24 +00:00
|
|
|
gid = namelookup(gidtab, d->gid);
|
|
|
|
if(gid == -1)
|
2017-04-28 15:41:48 +00:00
|
|
|
return -1;
|
|
|
|
}else
|
|
|
|
gid = -1;
|
|
|
|
fl |= SSH_FILEXFER_ATTR_UIDGID;
|
|
|
|
rc = pack(p, e - p, "uu", uid, gid); if(rc < 0) return -1; p += rc;
|
|
|
|
}
|
|
|
|
if(d->mode != (ulong)-1){
|
|
|
|
fl |= SSH_FILEXFER_ATTR_PERMISSIONS;
|
|
|
|
rc = pack(p, e - p, "u", d->mode); if(rc < 0) return -1; p += rc;
|
|
|
|
}
|
|
|
|
if(d->atime != (ulong)-1 || d->mtime != (ulong)-1){
|
|
|
|
/* FIXME: see above */
|
|
|
|
fl |= SSH_FILEXFER_ATTR_ACMODTIME;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-04-28 20:42:38 +00:00
|
|
|
int
|
2017-09-22 09:48:41 +00:00
|
|
|
attrfixupqid(Qid *qid)
|
2017-04-28 20:42:38 +00:00
|
|
|
{
|
2017-09-22 09:48:41 +00:00
|
|
|
u32int flags;
|
2017-04-28 20:42:38 +00:00
|
|
|
uchar *p;
|
2017-09-22 09:48:41 +00:00
|
|
|
|
|
|
|
if(unpack(rxpkt, rxlen, "_____u", &flags) < 0) return -1;
|
2017-04-28 20:42:38 +00:00
|
|
|
p = rxpkt + 9;
|
2017-09-22 09:48:41 +00:00
|
|
|
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;
|
2018-05-27 11:06:07 +00:00
|
|
|
else qid->type = QTDIR;
|
2017-09-22 09:48:41 +00:00
|
|
|
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;
|
2017-04-28 20:42:38 +00:00
|
|
|
}
|
|
|
|
|
2017-04-28 15:41:48 +00:00
|
|
|
int
|
|
|
|
parsedir(SFid *sf)
|
|
|
|
{
|
|
|
|
int i, rc;
|
|
|
|
Dir *d;
|
|
|
|
u32int c;
|
|
|
|
uchar *p, *ep;
|
|
|
|
char *fn, *ln;
|
|
|
|
int fns, lns;
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
if(unpack(rxpkt, rxlen, "_____u", &c) < 0) return -1;
|
|
|
|
wlock(sf);
|
2017-04-28 18:21:03 +00:00
|
|
|
freedir(sf);
|
|
|
|
sf->dirent = emalloc9p(c * sizeof(Dir));
|
|
|
|
d = sf->dirent;
|
2017-04-28 15:41:48 +00:00
|
|
|
p = rxpkt + 9;
|
|
|
|
ep = rxpkt + rxlen;
|
|
|
|
for(i = 0; i < c; i++){
|
|
|
|
memset(d, 0, sizeof(Dir));
|
2017-04-30 16:28:06 +00:00
|
|
|
rc = unpack(p, ep - p, "ss", &fn, &fns, &ln, &lns); if(rc < 0) goto err; p += rc;
|
2017-04-28 15:41:48 +00:00
|
|
|
rc = attrib2dir(p, ep, d); if(rc < 0) goto err; p += rc;
|
|
|
|
if(fn[0] == '.' && (fns == 1 || fns == 2 && fn[1] == '.')){
|
2017-04-30 16:28:06 +00:00
|
|
|
freedir1(d);
|
2017-04-28 15:41:48 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
d->name = emalloc9p(fns + 1);
|
|
|
|
memcpy(d->name, fn, fns);
|
2017-04-30 16:28:06 +00:00
|
|
|
d->name[fns] = 0;
|
2017-04-28 15:41:48 +00:00
|
|
|
s = pathcat(sf->fn, d->name);
|
|
|
|
d->qid.path = qidcalc(s);
|
|
|
|
free(s);
|
|
|
|
sf->ndirent++;
|
|
|
|
d++;
|
|
|
|
}
|
|
|
|
wunlock(sf);
|
|
|
|
return 0;
|
|
|
|
err:
|
2017-04-30 16:28:06 +00:00
|
|
|
freedir1(d);
|
2017-04-28 15:41:48 +00:00
|
|
|
wunlock(sf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-04-28 18:21:03 +00:00
|
|
|
|
2017-04-28 15:41:48 +00:00
|
|
|
void
|
|
|
|
readprocess(Req *r)
|
|
|
|
{
|
2017-04-28 18:21:03 +00:00
|
|
|
int i;
|
2017-04-28 15:41:48 +00:00
|
|
|
uchar *p, *ep;
|
|
|
|
uint rv;
|
|
|
|
SFid *sf;
|
2017-04-28 18:21:03 +00:00
|
|
|
|
|
|
|
sf = r->fid->aux;
|
|
|
|
wlock(sf);
|
|
|
|
if(sf->direof){
|
|
|
|
wunlock(sf);
|
|
|
|
respond(r, nil);
|
2017-04-28 15:41:48 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-04-28 18:21:03 +00:00
|
|
|
i = sf->dirpos;
|
|
|
|
p = (uchar*)r->ofcall.data + r->ofcall.count;
|
|
|
|
ep = (uchar*)r->ofcall.data + r->ifcall.count;
|
|
|
|
rv = ep - p;
|
2017-04-28 15:41:48 +00:00
|
|
|
while(p < ep){
|
2017-04-28 18:21:03 +00:00
|
|
|
if(i >= sf->ndirent)
|
|
|
|
break;
|
|
|
|
rv = convD2M(&sf->dirent[i], p, ep-p);
|
2017-04-28 15:41:48 +00:00
|
|
|
if(rv <= BIT16SZ)
|
|
|
|
break;
|
|
|
|
p += rv;
|
2017-04-28 18:21:03 +00:00
|
|
|
i++;
|
2017-04-28 15:41:48 +00:00
|
|
|
}
|
2017-04-28 18:21:03 +00:00
|
|
|
sf->dirpos = i;
|
|
|
|
if(i >= sf->ndirent)
|
|
|
|
freedir(sf);
|
|
|
|
wunlock(sf);
|
2017-04-28 15:41:48 +00:00
|
|
|
r->ofcall.count = p - (uchar*)r->ofcall.data;
|
2017-04-28 18:21:03 +00:00
|
|
|
if(rv <= BIT16SZ)
|
|
|
|
respond(r, nil);
|
|
|
|
else
|
|
|
|
submitreq(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){
|
|
|
|
wlock(sf);
|
|
|
|
freedir(sf);
|
|
|
|
if(sf->dirreads > 0){
|
|
|
|
r->aux = (void*)-1;
|
|
|
|
submitreq(r);
|
|
|
|
wunlock(sf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
wunlock(sf);
|
|
|
|
}
|
|
|
|
readprocess(r);
|
2017-04-28 15:41:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sshfsattach(Req *r)
|
|
|
|
{
|
|
|
|
SFid *sf;
|
|
|
|
|
2018-05-27 11:06:07 +00:00
|
|
|
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;
|
2017-04-28 15:41:48 +00:00
|
|
|
submitreq(r);
|
2018-05-27 11:06:07 +00:00
|
|
|
}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);
|
2017-04-28 15:41:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sendproc(void *)
|
|
|
|
{
|
|
|
|
SReq *r;
|
|
|
|
SFid *sf;
|
|
|
|
int i;
|
|
|
|
int x, y;
|
|
|
|
char *s, *t;
|
|
|
|
|
|
|
|
threadsetname("send");
|
|
|
|
|
|
|
|
for(;;){
|
|
|
|
qlock(&sreqwrlock);
|
|
|
|
while(sreqwr == nil)
|
|
|
|
rsleep(&writerend);
|
|
|
|
r = sreqwr;
|
|
|
|
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);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(r->req == nil)
|
|
|
|
sysfatal("nil request in queue");
|
|
|
|
|
|
|
|
sf = r->req->fid != nil ? r->req->fid->aux : nil;
|
|
|
|
switch(r->req->ifcall.type){
|
|
|
|
case Tattach:
|
2018-05-27 11:06:07 +00:00
|
|
|
sendpkt("bus", SSH_FXP_STAT, r->reqid, sf->fn, strlen(sf->fn));
|
2017-04-28 15:41:48 +00:00
|
|
|
break;
|
|
|
|
case Twalk:
|
|
|
|
sendpkt("bus", SSH_FXP_STAT, r->reqid, 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{
|
|
|
|
x = r->req->ifcall.mode;
|
|
|
|
y = 0;
|
|
|
|
switch(x & 3){
|
|
|
|
case OREAD: y = SSH_FXF_READ; break;
|
|
|
|
case OWRITE: y = SSH_FXF_WRITE; break;
|
|
|
|
case ORDWR: y = SSH_FXF_READ | SSH_FXF_WRITE; break;
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
runlock(sf);
|
|
|
|
break;
|
|
|
|
case Tcreate:
|
|
|
|
rlock(sf);
|
|
|
|
s = pathcat(sf->fn, r->req->ifcall.name);
|
|
|
|
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;
|
|
|
|
}else{
|
|
|
|
sendpkt("bus", SSH_FXP_OPENDIR, r->reqid, s, strlen(s));
|
|
|
|
r->req->aux = (void*)-2;
|
|
|
|
}
|
|
|
|
free(s);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
x = r->req->ifcall.mode;
|
|
|
|
y = SSH_FXF_CREAT | SSH_FXF_EXCL;
|
|
|
|
switch(x & 3){
|
|
|
|
case OREAD: y |= SSH_FXF_READ; break;
|
|
|
|
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,
|
|
|
|
SSH_FILEXFER_ATTR_PERMISSIONS, r->req->ifcall.perm & 0777);
|
|
|
|
free(s);
|
|
|
|
break;
|
|
|
|
case Tread:
|
2017-04-28 18:21:03 +00:00
|
|
|
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++;
|
2017-04-30 16:28:06 +00:00
|
|
|
sendpkt("bus", SSH_FXP_READDIR, r->reqid, sf->hand, sf->handn);
|
2017-04-28 18:21:03 +00:00
|
|
|
}
|
|
|
|
wunlock(sf);
|
|
|
|
}else{
|
|
|
|
rlock(sf);
|
2017-04-28 15:41:48 +00:00
|
|
|
sendpkt("busvuu", SSH_FXP_READ, r->reqid, sf->hand, sf->handn,
|
|
|
|
r->req->ifcall.offset, r->req->ifcall.count);
|
2017-04-28 18:21:03 +00:00
|
|
|
runlock(sf);
|
|
|
|
}
|
2017-04-28 15:41:48 +00:00
|
|
|
break;
|
|
|
|
case Twrite:
|
|
|
|
x = r->req->ifcall.count - r->req->ofcall.count;
|
|
|
|
if(x >= MAXWRITE) x = MAXWRITE;
|
|
|
|
rlock(sf);
|
|
|
|
sendpkt("busvs", SSH_FXP_WRITE, r->reqid, 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;
|
2017-04-28 20:42:38 +00:00
|
|
|
if(sf->handn > 0 && (sf->qid.type & QTDIR) == 0)
|
2017-04-28 15:41:48 +00:00
|
|
|
sendpkt("bus", SSH_FXP_FSTAT, r->reqid, sf->hand, sf->handn);
|
|
|
|
else
|
|
|
|
sendpkt("bus", SSH_FXP_STAT, r->reqid, sf->fn, strlen(sf->fn));
|
|
|
|
runlock(sf);
|
|
|
|
break;
|
|
|
|
case Twstat:
|
|
|
|
if(r->req->aux == (void *) -1){
|
|
|
|
rlock(sf);
|
|
|
|
s = parentdir(sf->fn);
|
|
|
|
t = pathcat(s, r->req->d.name);
|
|
|
|
free(s);
|
|
|
|
r->req->aux = t;
|
2017-04-30 16:28:06 +00:00
|
|
|
sendpkt("buss", SSH_FXP_RENAME, r->reqid, sf->fn, strlen(sf->fn), t, strlen(t));
|
2017-04-28 15:41:48 +00:00
|
|
|
runlock(sf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
x = dir2attrib(&r->req->d, (uchar **) &s);
|
|
|
|
if(x < 0){
|
|
|
|
responderror(r->req);
|
|
|
|
putsreq(r);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rlock(sf);
|
|
|
|
if(sf->handn > 0)
|
|
|
|
sendpkt("bus[", SSH_FXP_FSETSTAT, r->reqid, sf->hand, sf->handn, s, x);
|
|
|
|
else
|
|
|
|
sendpkt("bus[", SSH_FXP_SETSTAT, r->reqid, sf->fn, strlen(sf->fn), s, x);
|
|
|
|
runlock(sf);
|
2017-09-22 09:48:41 +00:00
|
|
|
free(s);
|
2017-04-28 15:41:48 +00:00
|
|
|
break;
|
|
|
|
case Tremove:
|
|
|
|
rlock(sf);
|
|
|
|
if((sf->qid.type & QTDIR) != 0)
|
|
|
|
sendpkt("bus", SSH_FXP_RMDIR, r->reqid, sf->fn, strlen(sf->fn));
|
|
|
|
else
|
|
|
|
sendpkt("bus", SSH_FXP_REMOVE, r->reqid, sf->fn, strlen(sf->fn));
|
|
|
|
runlock(sf);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprint(2, "sendproc: unimplemented 9p request %F in queue\n", &r->req->ifcall);
|
|
|
|
respond(r->req, "phase error");
|
|
|
|
putsreq(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
recvproc(void *)
|
|
|
|
{
|
|
|
|
static char ebuf[256];
|
|
|
|
|
|
|
|
SReq *r;
|
|
|
|
SFid *sf;
|
2017-09-22 09:48:41 +00:00
|
|
|
int t, id;
|
2017-04-28 15:41:48 +00:00
|
|
|
u32int code;
|
2018-03-04 19:52:35 +00:00
|
|
|
char *msg, *lang, *hand, *s;
|
2017-04-28 15:41:48 +00:00
|
|
|
int msgn, langn, handn;
|
2017-04-28 18:21:03 +00:00
|
|
|
int okresp;
|
2017-04-28 15:41:48 +00:00
|
|
|
char *e;
|
|
|
|
|
|
|
|
threadsetname("recv");
|
|
|
|
|
|
|
|
for(;;){
|
|
|
|
e = "phase error";
|
|
|
|
switch(t = recvpkt()){
|
|
|
|
case SSH_FXP_STATUS:
|
|
|
|
case SSH_FXP_HANDLE:
|
|
|
|
case SSH_FXP_DATA:
|
|
|
|
case SSH_FXP_NAME:
|
|
|
|
case SSH_FXP_ATTRS:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprint(2, "sshfs: received unexpected packet of type %Σ\n", t);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
id = GET4(rxpkt + 1);
|
|
|
|
if(id >= MAXREQID){
|
2017-04-28 18:45:32 +00:00
|
|
|
fprint(2, "sshfs: received %Σ response with id out of range, %d > %d\n", t, id, MAXREQID);
|
2017-04-28 15:41:48 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
qlock(&sreqidlock);
|
|
|
|
r = sreqrd[id];
|
|
|
|
if(r != nil){
|
|
|
|
sreqrd[id] = nil;
|
2017-04-28 18:45:32 +00:00
|
|
|
r->reqid = -1;
|
2017-04-28 15:41:48 +00:00
|
|
|
rwakeup(&sreqidrend);
|
|
|
|
}
|
|
|
|
qunlock(&sreqidlock);
|
|
|
|
if(r == nil){
|
2017-04-28 18:45:32 +00:00
|
|
|
fprint(2, "sshfs: received %Σ response to non-existent request (req id = %d)\n", t, id);
|
2017-04-28 15:41:48 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(r->closefid != nil){
|
|
|
|
putsreq(r);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(r->req == nil)
|
|
|
|
sysfatal("recvproc: r->req == nil");
|
|
|
|
|
|
|
|
sf = r->req->fid != nil ? r->req->fid->aux : nil;
|
2017-04-28 18:21:03 +00:00
|
|
|
okresp = rxlen >= 9 && t == SSH_FXP_STATUS && GET4(rxpkt+5) == SSH_FX_OK;
|
2017-04-28 15:41:48 +00:00
|
|
|
switch(r->req->ifcall.type){
|
|
|
|
case Tattach:
|
|
|
|
if(t != SSH_FXP_ATTRS) goto common;
|
2017-09-22 09:48:41 +00:00
|
|
|
if(attrfixupqid(&r->req->ofcall.qid) < 0)
|
2017-04-28 20:42:38 +00:00
|
|
|
goto garbage;
|
2017-09-22 09:48:41 +00:00
|
|
|
r->req->aux = (void*)-1;
|
|
|
|
if((r->req->ofcall.qid.type & QTDIR) == 0)
|
2017-04-28 15:41:48 +00:00
|
|
|
respond(r->req, "not a directory");
|
|
|
|
else
|
|
|
|
sshfsattach(r->req);
|
|
|
|
break;
|
|
|
|
case Twalk:
|
|
|
|
if(t != SSH_FXP_ATTRS) goto common;
|
2017-09-22 09:48:41 +00:00
|
|
|
if(r->req->ofcall.nwqid <= 0
|
|
|
|
|| attrfixupqid(&r->req->ofcall.wqid[r->req->ofcall.nwqid - 1]) < 0)
|
|
|
|
goto garbage;
|
|
|
|
walkprocess(r->req, nil);
|
2017-04-28 15:41:48 +00:00
|
|
|
break;
|
|
|
|
case Tcreate:
|
2017-04-28 18:21:03 +00:00
|
|
|
if(okresp && r->req->aux == (void*)-1){
|
2017-04-28 15:41:48 +00:00
|
|
|
submitreq(r->req);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* wet floor */
|
2017-04-28 18:21:03 +00:00
|
|
|
case Topen: opendir:
|
2017-04-28 15:41:48 +00:00
|
|
|
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);
|
2018-03-04 19:52:35 +00:00
|
|
|
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;
|
|
|
|
}
|
2017-04-28 15:41:48 +00:00
|
|
|
wunlock(sf);
|
2017-04-28 18:21:03 +00:00
|
|
|
if(r->req->ifcall.type == Tread){
|
|
|
|
r->req->aux = nil;
|
|
|
|
readprocess(r->req);
|
|
|
|
}else
|
|
|
|
respond(r->req, nil);
|
2017-04-28 15:41:48 +00:00
|
|
|
break;
|
|
|
|
case Tread:
|
|
|
|
if((r->req->fid->qid.type & QTDIR) != 0){
|
2017-04-28 18:21:03 +00:00
|
|
|
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);
|
|
|
|
}
|
2017-04-28 15:41:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(t != SSH_FXP_DATA) goto common;
|
|
|
|
if(unpack(rxpkt, rxlen, "_____s", &msg, &msgn) < 0) goto garbage;
|
|
|
|
if(msgn > r->req->ifcall.count) msgn = r->req->ifcall.count;
|
|
|
|
r->req->ofcall.count = msgn;
|
|
|
|
memcpy(r->req->ofcall.data, msg, msgn);
|
|
|
|
respond(r->req, nil);
|
|
|
|
break;
|
|
|
|
case Twrite:
|
|
|
|
if(t != SSH_FXP_STATUS) goto common;
|
2017-04-28 18:21:03 +00:00
|
|
|
if(okresp){
|
2017-04-28 15:41:48 +00:00
|
|
|
r->req->ofcall.count += r->req->ofcall.offset;
|
|
|
|
if(r->req->ofcall.count == r->req->ifcall.count)
|
|
|
|
respond(r->req, nil);
|
|
|
|
else
|
|
|
|
submitreq(r->req);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(r->req->ofcall.count == 0) goto common;
|
|
|
|
respond(r->req, nil);
|
|
|
|
break;
|
|
|
|
case Tstat:
|
|
|
|
if(t != SSH_FXP_ATTRS) goto common;
|
|
|
|
if(attrib2dir(rxpkt + 5, rxpkt + rxlen, &r->req->d) < 0) goto garbage;
|
|
|
|
respond(r->req, nil);
|
|
|
|
break;
|
|
|
|
case Twstat:
|
2017-04-28 18:21:03 +00:00
|
|
|
if(!okresp) goto common;
|
2017-09-22 09:48:41 +00:00
|
|
|
if(!r->req->d.name[0]){
|
|
|
|
respond(r->req, nil);
|
|
|
|
break;
|
|
|
|
}
|
2017-04-28 15:41:48 +00:00
|
|
|
if(r->req->aux == nil){
|
|
|
|
r->req->aux = (void *) -1;
|
|
|
|
submitreq(r->req);
|
|
|
|
}else{
|
|
|
|
wlock(sf);
|
|
|
|
free(sf->fn);
|
|
|
|
sf->fn = r->req->aux;
|
|
|
|
wunlock(sf);
|
|
|
|
respond(r->req, nil);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Tremove:
|
|
|
|
goto common;
|
|
|
|
default:
|
|
|
|
fprint(2, "sendproc: unimplemented 9p request %F in queue\n", &r->req->ifcall);
|
|
|
|
respond(r->req, "phase error");
|
|
|
|
}
|
|
|
|
putsreq(r);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
common:
|
|
|
|
switch(t){
|
|
|
|
case SSH_FXP_STATUS:
|
|
|
|
if(unpack(rxpkt, rxlen, "_____uss", &code, &msg, &msgn, &lang, &langn) < 0){
|
|
|
|
garbage:
|
|
|
|
fprint(2, "sshfs: garbled packet in response to 9p request %F\n", &r->req->ifcall);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(code == SSH_FX_OK)
|
|
|
|
e = nil;
|
|
|
|
else if(code == SSH_FX_EOF && r->req->ifcall.type == Tread){
|
|
|
|
if((r->req->fid->qid.type & QTDIR) != 0){
|
|
|
|
wlock(sf);
|
|
|
|
sf->direof = 1;
|
|
|
|
wunlock(sf);
|
|
|
|
readprocess(r->req);
|
|
|
|
putsreq(r);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
r->req->ofcall.count = 0;
|
|
|
|
e = nil;
|
|
|
|
}else if(msgn > 0){
|
|
|
|
e = msg;
|
|
|
|
e[msgn] = 0;
|
|
|
|
}else if(code < nelem(errors))
|
|
|
|
e = errors[code];
|
|
|
|
else{
|
|
|
|
snprint(ebuf, sizeof(ebuf), "error code %d", code);
|
|
|
|
e = ebuf;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprint(2, "sshfs: received unexpected packet %Σ for 9p request %F\n", t, &r->req->ifcall);
|
|
|
|
}
|
|
|
|
if(r->req->ifcall.type == Twalk)
|
2017-09-22 09:48:41 +00:00
|
|
|
walkprocess(r->req, e);
|
2017-04-28 15:41:48 +00:00
|
|
|
else
|
|
|
|
respond(r->req, e);
|
|
|
|
putsreq(r);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sshfswalk(Req *r)
|
|
|
|
{
|
|
|
|
SFid *s, *t;
|
|
|
|
char *p, *q;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if(r->fid != r->newfid){
|
|
|
|
r->newfid->qid = r->fid->qid;
|
|
|
|
s = r->fid->aux;
|
|
|
|
t = emalloc9p(sizeof(SFid));
|
2017-04-30 16:28:06 +00:00
|
|
|
t->fn = estrdup9p(s->fn);
|
2017-04-28 15:41:48 +00:00
|
|
|
t->qid = s->qid;
|
|
|
|
r->newfid->aux = t;
|
|
|
|
}else
|
|
|
|
t = r->fid->aux;
|
|
|
|
if(r->ifcall.nwname == 0){
|
|
|
|
respond(r, nil);
|
|
|
|
return;
|
|
|
|
}
|
2017-04-30 16:28:06 +00:00
|
|
|
p = estrdup9p(t->fn);
|
2017-04-28 15:41:48 +00:00
|
|
|
for(i = 0; i < r->ifcall.nwname; i++){
|
2017-04-30 16:28:06 +00:00
|
|
|
q = pathcat(p, r->ifcall.wname[i]);
|
2017-04-28 15:41:48 +00:00
|
|
|
free(p);
|
|
|
|
p = q;
|
|
|
|
r->ofcall.wqid[i] = (Qid){qidcalc(p), 0, QTDIR};
|
|
|
|
}
|
|
|
|
r->ofcall.nwqid = r->ifcall.nwname;
|
|
|
|
r->aux = p;
|
|
|
|
submitreq(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sshfsdestroyfid(Fid *f)
|
|
|
|
{
|
|
|
|
SFid *sf;
|
|
|
|
SReq *sr;
|
|
|
|
|
|
|
|
sf = f->aux;
|
|
|
|
if(sf == nil)
|
|
|
|
return;
|
|
|
|
if(sf->hand != nil){
|
|
|
|
sr = emalloc9p(sizeof(SReq));
|
|
|
|
sr->reqid = -1;
|
|
|
|
sr->closefid = sf;
|
|
|
|
submitsreq(sr);
|
|
|
|
}else
|
|
|
|
putsfid(sf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sshfsdestroyreq(Req *r)
|
|
|
|
{
|
|
|
|
if(r->ifcall.type == Twalk)
|
|
|
|
free(r->aux);
|
|
|
|
}
|
|
|
|
|
2017-08-29 17:49:38 +00:00
|
|
|
void
|
|
|
|
sshfsstart(Srv *)
|
|
|
|
{
|
|
|
|
proccreate(sendproc, nil, mainstacksize);
|
|
|
|
proccreate(recvproc, nil, mainstacksize);
|
|
|
|
}
|
|
|
|
|
2017-04-28 15:41:48 +00:00
|
|
|
void
|
|
|
|
sshfsend(Srv *)
|
|
|
|
{
|
|
|
|
dprint("sshfs: ending\n");
|
|
|
|
threadexitsall(nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
Srv sshfssrv = {
|
2017-08-29 17:49:38 +00:00
|
|
|
.start sshfsstart,
|
2017-04-28 15:41:48 +00:00
|
|
|
.attach sshfsattach,
|
|
|
|
.walk sshfswalk,
|
|
|
|
.open submitreq,
|
|
|
|
.create submitreq,
|
2017-04-28 18:21:03 +00:00
|
|
|
.read sshfsread,
|
2017-04-28 15:41:48 +00:00
|
|
|
.write submitreq,
|
|
|
|
.stat submitreq,
|
|
|
|
.wstat submitreq,
|
|
|
|
.remove submitreq,
|
|
|
|
.destroyfid sshfsdestroyfid,
|
|
|
|
.destroyreq sshfsdestroyreq,
|
2017-08-29 17:49:38 +00:00
|
|
|
.end sshfsend,
|
2017-04-28 15:41:48 +00:00
|
|
|
};
|
|
|
|
|
2017-04-28 16:34:24 +00:00
|
|
|
char *
|
|
|
|
readfile(char *fn)
|
|
|
|
{
|
|
|
|
char *hand, *dat;
|
|
|
|
int handn, datn;
|
|
|
|
u32int code;
|
|
|
|
char *p;
|
|
|
|
int off;
|
|
|
|
|
|
|
|
if(fn == nil) return nil;
|
|
|
|
sendpkt("busuu", SSH_FXP_OPEN, 0, fn, strlen(fn), SSH_FXF_READ, 0);
|
|
|
|
if(recvpkt() != SSH_FXP_HANDLE) return nil;
|
|
|
|
if(unpack(rxpkt, rxlen, "_____s", &dat, &handn) < 0) return nil;
|
|
|
|
hand = emalloc9p(handn);
|
|
|
|
memcpy(hand, dat, handn);
|
|
|
|
off = 0;
|
|
|
|
p = nil;
|
|
|
|
for(;;){
|
|
|
|
sendpkt("busvu", SSH_FXP_READ, 0, hand, handn, (uvlong)off, MAXWRITE);
|
|
|
|
switch(recvpkt()){
|
|
|
|
case SSH_FXP_STATUS:
|
|
|
|
if(unpack(rxpkt, rxlen, "_____u", &code) < 0) goto err;
|
|
|
|
if(code == SSH_FX_EOF) goto out;
|
|
|
|
default:
|
|
|
|
goto err;
|
|
|
|
case SSH_FXP_DATA:
|
|
|
|
if(unpack(rxpkt, rxlen, "_____s", &dat, &datn) < 0) goto err;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
p = erealloc9p(p, off + datn + 1);
|
|
|
|
memcpy(p + off, dat, datn);
|
|
|
|
off += datn;
|
|
|
|
p[off] = 0;
|
|
|
|
}
|
|
|
|
err:
|
|
|
|
p = nil;
|
|
|
|
out:
|
|
|
|
sendpkt("bus", SSH_FXP_CLOSE, 0, hand, handn);
|
|
|
|
free(hand);
|
|
|
|
recvpkt();
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
passwdparse(IDEnt **tab, char *s)
|
|
|
|
{
|
|
|
|
IDEnt *e, **b;
|
2017-04-29 14:25:48 +00:00
|
|
|
char *p, *n;
|
|
|
|
int id;
|
2017-04-28 16:34:24 +00:00
|
|
|
|
2017-04-29 14:25:48 +00:00
|
|
|
if(s == nil)
|
|
|
|
return;
|
|
|
|
for(p = s;;){
|
2017-04-28 16:34:24 +00:00
|
|
|
n = p;
|
|
|
|
p = strpbrk(p, ":\n"); if(p == nil) break; if(*p != ':'){ p++; continue; }
|
|
|
|
*p = 0;
|
|
|
|
p = strpbrk(p+1, ":\n");
|
|
|
|
p = strpbrk(p, ":\n"); if(p == nil) break; if(*p != ':'){ p++; continue; }
|
|
|
|
id = strtol(p+1, &p, 10);
|
|
|
|
p = strchr(p, '\n');
|
|
|
|
if(p == nil) break;
|
|
|
|
p++;
|
|
|
|
e = emalloc9p(sizeof(IDEnt));
|
2017-04-30 16:28:06 +00:00
|
|
|
e->name = estrdup9p(n);
|
2017-04-28 16:34:24 +00:00
|
|
|
e->id = id;
|
|
|
|
b = &tab[((ulong)e->id) % HASH];
|
|
|
|
e->next = *b;
|
|
|
|
*b = e;
|
|
|
|
}
|
|
|
|
free(s);
|
|
|
|
}
|
|
|
|
|
2017-04-28 15:41:48 +00:00
|
|
|
int pfd[2];
|
|
|
|
int sshargc;
|
|
|
|
char **sshargv;
|
|
|
|
|
|
|
|
void
|
|
|
|
startssh(void *)
|
|
|
|
{
|
|
|
|
char *f;
|
|
|
|
|
|
|
|
close(pfd[0]);
|
|
|
|
dup(pfd[1], 0);
|
|
|
|
dup(pfd[1], 1);
|
|
|
|
close(pfd[1]);
|
|
|
|
if(strncmp(sshargv[0], "./", 2) != 0)
|
|
|
|
f = smprint("/bin/%s", sshargv[0]);
|
|
|
|
else
|
|
|
|
f = sshargv[0];
|
|
|
|
procexec(nil, f, sshargv);
|
|
|
|
sysfatal("exec: %r");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
usage(void)
|
|
|
|
{
|
2017-04-28 16:34:24 +00:00
|
|
|
static char *common = "[-abdRUG] [-s service] [-m mtpt] [-u uidfile] [-g gidfile]";
|
2018-03-20 20:51:04 +00:00
|
|
|
fprint(2, "usage: %s %s [-- ssh-options] [user@]host\n", argv0, common);
|
2017-04-28 16:34:24 +00:00
|
|
|
fprint(2, " %s %s -c cmdline\n", argv0, common);
|
|
|
|
fprint(2, " %s %s -p\n", argv0, common);
|
2017-04-28 15:41:48 +00:00
|
|
|
exits("usage");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
threadmain(int argc, char **argv)
|
|
|
|
{
|
|
|
|
u32int x;
|
|
|
|
static int pflag, cflag;
|
|
|
|
static char *svc, *mtpt;
|
|
|
|
static int mflag;
|
2017-04-28 16:34:24 +00:00
|
|
|
static char *uidfile, *gidfile;
|
2017-04-28 15:41:48 +00:00
|
|
|
|
|
|
|
fmtinstall(L'Σ', fxpfmt);
|
|
|
|
|
|
|
|
mtpt = "/n/ssh";
|
2017-04-28 16:34:24 +00:00
|
|
|
uidfile = "/etc/passwd";
|
|
|
|
gidfile = "/etc/group";
|
2017-04-28 15:41:48 +00:00
|
|
|
ARGBEGIN{
|
|
|
|
case 'R': readonly++; break;
|
|
|
|
case 'd': debug++; chatty9p++; break;
|
|
|
|
case 'p': pflag++; break;
|
|
|
|
case 'c': cflag++; break;
|
|
|
|
case 's': svc = EARGF(usage()); break;
|
|
|
|
case 'a': mflag |= MAFTER; break;
|
|
|
|
case 'b': mflag |= MBEFORE; break;
|
|
|
|
case 'm': mtpt = EARGF(usage()); break;
|
2017-04-29 18:44:01 +00:00
|
|
|
case 'M': mtpt = nil; break;
|
2017-04-28 16:34:24 +00:00
|
|
|
case 'u': uidfile = EARGF(usage()); break;
|
|
|
|
case 'U': uidfile = nil; break;
|
|
|
|
case 'g': gidfile = EARGF(usage()); break;
|
|
|
|
case 'G': gidfile = nil; break;
|
2017-04-29 18:44:01 +00:00
|
|
|
case 'r': root = EARGF(usage()); break;
|
2017-04-28 15:41:48 +00:00
|
|
|
default: usage();
|
|
|
|
}ARGEND;
|
|
|
|
|
|
|
|
if(readonly){
|
|
|
|
sshfssrv.create = nil;
|
|
|
|
sshfssrv.write = nil;
|
|
|
|
sshfssrv.wstat = nil;
|
|
|
|
sshfssrv.remove = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pflag){
|
|
|
|
rdfd = 0;
|
|
|
|
wrfd = 1;
|
|
|
|
}else{
|
|
|
|
if(argc == 0) usage();
|
|
|
|
if(cflag){
|
|
|
|
sshargc = argc;
|
|
|
|
sshargv = argv;
|
|
|
|
}else{
|
|
|
|
sshargc = argc + 2;
|
|
|
|
sshargv = emalloc9p(sizeof(char *) * (sshargc + 1));
|
|
|
|
sshargv[0] = "ssh";
|
|
|
|
memcpy(sshargv + 1, argv, argc * sizeof(char *));
|
|
|
|
sshargv[sshargc - 1] = "#sftp";
|
|
|
|
}
|
|
|
|
pipe(pfd);
|
|
|
|
rdfd = wrfd = pfd[0];
|
2017-08-29 19:22:31 +00:00
|
|
|
procrfork(startssh, nil, mainstacksize, RFFDG|RFNOTEG|RFNAMEG);
|
2017-04-28 15:41:48 +00:00
|
|
|
close(pfd[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
sendpkt("bu", SSH_FXP_INIT, VERSION);
|
|
|
|
if(recvpkt() != SSH_FXP_VERSION || unpack(rxpkt, rxlen, "_u", &x) < 0) sysfatal("received garbage");
|
|
|
|
if(x != VERSION) sysfatal("server replied with incompatible version %d", x);
|
|
|
|
|
2017-04-28 16:34:24 +00:00
|
|
|
passwdparse(uidtab, readfile(uidfile));
|
|
|
|
passwdparse(gidtab, readfile(gidfile));
|
|
|
|
|
2017-04-28 15:41:48 +00:00
|
|
|
threadpostmountsrv(&sshfssrv, svc, mtpt, MCREATE | mflag);
|
|
|
|
}
|