/* Copyright © 2008 Fazlul Shahriar*/
#include "fxp.h"
typedef struct FAux FAux;
struct FAux {
char *path;
FHandle *handle;
Dir **dir;
int ndir;
};
static ulong Seed;
/*
* http://9fans.net/archive/2003/05/56
* http://9fans.net/archive/2003/05/57
*/
static uvlong
hash(char *p, char *n, ulong seed)
{
ulong x;
uvlong xx;
x = seed;
while(*p)
x = x*1103515245 + 12345 + *p++;
if(n){
x = x*1103515245 + 12345 + '/'; /* for neatness only */
while(*n)
x = x*1103515245 + 12345 + *n++;
}
xx = x;
xx <<= 32;
xx |= seed;
return xx;
}
static Qid
getqid(char *path, char *name, Dir *d)
{
Qid q;
q.path = hash(path, name, Seed);
q.type = d->mode&DMDIR ? QTDIR : QTFILE;
q.vers = d->mtime;
return q;
}
FAux*
newfaux(char *path)
{
FAux *fa;
fa = emalloc9p(sizeof *fa);
fa->path = estrdup9p(path);
fa->handle = nil;
fa->dir = nil;
fa->ndir = 0;
return fa;
}
void
freefaux(FAux *fa)
{
int i;
free(fa->path);
if(fa->dir != nil){
for(i = 0; i < fa->ndir; i++)
freedir(fa->dir[i]);
free(fa->dir);
}
free(fa);
}
static void
fsattach(Req *r)
{
FAux *fa;
Dir *d;
if((d = fxpstat("/")) == nil){
responderror(r);
return;
}
r->fid->qid = getqid("/", "", d);
freedir(d);
r->ofcall.qid = r->fid->qid;
fa = newfaux("/");
r->fid->aux = fa;
respond(r, nil);
}
static char*
fsclone(Fid *oldfid, Fid *newfid)
{
FAux *fa, *ofa;
ofa = oldfid->aux;
if(ofa->handle != nil)
return Ehand;
fa = newfaux(ofa->path);
newfid->aux = fa;
return nil;
}
static char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
FAux *fa;
char *s, *dot, *err;
Dir *d;
fa = fid->aux;
if(fa->handle != nil)
return Ehand;
if(strcmp(name, "..") == 0){
s = strrchr(fa->path, '/');
if(s == nil)
return Epath;
if(s != fa->path) /* not / */
*s = 0;
}else
fa->path = eappend(fa->path, "/", name);
d = fxpstat1(fa->path, &err);
if(d == nil)
return err;
fid->qid = getqid(fa->path, "", d);
*qid = fid->qid;
freedir(d);
if(d->mode&DMDIR){
/*
* /some/path/ is executable iff /some/path/. is
* stat-able
*/
dot = estrstrdup(fa->path, "/.");
if((d = fxpstat1(dot, &err)) == nil)
return err;
freedir(d);
free(dot);
}
return nil;
}
static void
fsdestroyfid(Fid *fid)
{
FAux *fa;
fa = fid->aux;
if(fa == nil)
return;
fid->aux = nil;
if(fa->handle != nil)
fxpclose(fa->handle);
freefaux(fa);
}
static void
fsopen(Req *r)
{
FAux *fa;
FHandle *h;
fa = r->fid->aux;
if(fa->handle != nil){
respond(r, Eopen);
return;
}
if(r->fid->qid.type & QTDIR)
h = fxpopendir(fa->path);
else
h = fxpopen(fa->path, r->ifcall.mode);
if(h == nil){
responderror(r);
return;
}
fa->handle = h;
respond(r, nil);
}
static void
fscreate(Req *r)
{
FAux *fa, *dir;
u32int perm;
uchar mode;
char *s;
Dir *d;
dir = r->fid->aux;
s = estr3dup(dir->path, "/", r->ifcall.name);
fa = newfaux(s);
free(s);
mode = r->ifcall.mode;
perm = r->ifcall.perm;
if(perm & DMDIR){
if(fxpmkdir(fa->path, perm) < 0){
freefaux(fa);
responderror(r);
return;
}
fa->handle = fxpopendir(fa->path);
}else{
fa->handle = fxpcreate(fa->path, mode, perm);
}
if(fa->handle == nil){
freefaux(fa);
responderror(r);
return;
}
if((d = fxpstat(fa->path)) == nil){
freefaux(fa);
responderror(r);
return;
}
r->fid->qid = getqid(fa->path, "", d);
freedir(d);
r->ofcall.qid = r->fid->qid;
r->fid->aux = fa;
respond(r, nil);
}
static void
fsremove(Req *r)
{
FAux *fa;
int n;
fa = r->fid->aux;
if(r->fid->qid.type & QTDIR)
n = fxprmdir(fa->path);
else
n = fxpremove(fa->path);
if(n < 0){
responderror(r);
return;
}
respond(r, nil);
}
static int
getdirent(int n, Dir *d, void *aux)
{
FAux *fa;
Dir *e;
fa = aux;
if(n >= fa->ndir)
return -1;
e = fa->dir[n];
d->qid = getqid(fa->path, e->name, e);
d->mode = e->mode;
d->atime = e->atime;
d->mtime = e->mtime;
d->length = e->length;
d->name = estrdup9p(e->name);
d->uid = estrdup9p(e->uid);
d->gid = estrdup9p(e->gid);
d->muid = estrdup9p(e->muid);
return 0;
}
static void
fsread(Req *r)
{
FAux *fa;
uchar *buf;
long msz, nbuf;
int n, nds, i;
vlong off;
Dir **d, **ds, **dp;
fa = r->fid->aux;
if(fa->handle == nil){
respond(r, Ehand);
return;
}
if(r->fid->qid.type & QTDIR){
if(fa->dir == nil){
ds = nil;
nds = 0;
while((n = fxpreaddir(fa->handle, &d)) > 0){
ds = erealloc9p(ds, (nds+n)*sizeof(Dir*));
dp = ds+nds;
for(i = 0; i < n; i++)
dp[i] = d[i];
free(d);
nds += n;
}
if(n < 0){
free(ds);
responderror(r);
return;
}
fa->dir = ds;
fa->ndir = nds;
}
dirread9p(r, getdirent, fa);
}else{
nbuf = r->ifcall.count;
buf = (uchar*)r->ofcall.data;
off = r->ifcall.offset;
if((msz = fxpread(fa->handle, buf, nbuf, off)) < 0){
free(buf);
responderror(r);
return;
}
r->ofcall.count = msz;
}
respond(r, nil);
}
static void
fswrite(Req *r)
{
FAux *fa;
long n;
fa = r->fid->aux;
if(fa->handle == nil){
respond(r, Ehand);
return;
}
n = fxpwrite(fa->handle, r->ifcall.data,
r->ifcall.count, r->ifcall.offset);
if(n < 0){
responderror(r);
return;
}
r->ofcall.count = n;
respond(r, nil);
}
static void
fsstat(Req *r)
{
FAux *fa;
Dir *d;
fa = r->fid->aux;
d = fxpstat(fa->path);
if(d == nil){
responderror(r);
return;
}
r->d.qid = getqid(fa->path, "", d);
r->d.mode = d->mode;
r->d.atime = d->atime;
r->d.mtime = d->mtime;
r->d.length = d->length;
r->d.name = estrdup9p(d->name);
r->d.uid = estrdup9p(d->uid);
r->d.gid = estrdup9p(d->gid);
r->d.muid = estrdup9p(d->muid);
freedir(d);
respond(r, nil);
}
static char*
dirname(char *path)
{
char *d, *s;
d = estrdup9p(path);
s = strrchr(d, '/');
if(s != nil)
*s = 0;
return d;
}
static void
fswstat(Req *r)
{
FAux *fa;
char *newp, *dir;
fa = r->fid->aux;
if(r->d.name[0] == 0){
if(fxpsetstat(fa->path, &r->d) < 0){
responderror(r);
return;
}
}else{
dir = dirname(fa->path);
newp = estr3dup(dir, "/", r->d.name);
free(dir);
if(strchr(fa->path, '/') == nil || strchr(newp, '/') == nil){
respond(r, Epath);
return;
}
if(fxprename(fa->path, newp) < 0){
free(newp);
responderror(r);
return;
}
free(fa->path);
fa->path = newp;
}
respond(r, nil);
}
static void
fsend(Srv*)
{
fxpterm();
}
static Srv fs = {
.attach = fsattach,
.walk1 = fswalk1,
.clone = fsclone,
.open = fsopen,
.create = fscreate,
.read = fsread,
.write = fswrite,
.remove = fsremove,
.wstat = fswstat,
.stat = fsstat,
.destroyfid = fsdestroyfid,
.end = fsend,
};
void
usage(void)
{
fprint(2, "usage: sftpfs [-12oDU] [-m mountpoint] [-p serverpath ] [-s srvname] [-u passwd group] [user@]hostname\n");
threadexitsall("usage");
}
static char *pfile;
static char *gfile;
static char *srvname;
static char *host;
static char *mntpt;
static char *serverpath = "/usr/lib/sftp-server";
static int bigu;
static char sshver = '2';
void
threadmain(int argc, char **argv)
{
char mpath[50], ppath[50], gpath[50];
ARGBEGIN{
default:
usage();
break;
case '1':
case '2':
case 'o':
sshver = ARGC();
break;
case 'D':
chatty9p++;
break;
case 'm':
mntpt = EARGF(usage());
break;
case 'p':
serverpath = EARGF(usage());
break;
case 's':
srvname = EARGF(usage());
break;
case 'u':
pfile = EARGF(usage());
gfile = EARGF(usage());
break;
case 'U':
bigu = 1;
break;
}ARGEND;
if(argc < 1)
usage();
host = argv[0];
if(fxpinit(host, sshver, serverpath) < 0)
sysfatal("sftp init: %r");
if(mntpt == nil){
snprint(mpath, sizeof(mpath), "/n/%s", host);
mntpt = mpath;
}
if(bigu){
snprint(ppath, sizeof(ppath), "%s/etc/passwd", mntpt);
pfile = ppath;
snprint(gpath, sizeof(gpath), "%s/etc/group", mntpt);
gfile = gpath;
}
/*
* try to ensure two mounts of the same host get the same qids,
* but two mounts of similar hosts get different ones.
*/
Seed = hash(host, nil, 0);
threadpostmountsrv(&fs, srvname, mntpt, MREPL|MCREATE);
if(gfile != nil && pfile != nil)
fxpreadmap(pfile, gfile);
threadexits(nil);
}
|