/*
* Copyright (c) 2013, Coraid, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Coraid nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CORAID BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "dat.h"
typedef struct Fdref Fdref;
typedef struct Fidaux Fidaux;
typedef struct Lmsg Lmsg;
typedef struct Srvaux Srvaux;
typedef struct Uglymap Uglymap;
struct Fdref {
Ref ref;
int fd;
};
struct Fidaux {
char *path;
char *uname;
uvlong lsearch;
int dirindex;
Fdref *store;
};
struct Lmsg {
int data;
char *rsys;
};
struct Srvaux {
Ioproc *io9p;
};
struct Uglymap {
Srv *s;
uchar *rbuf;
Uglymap *next;
};
static void θattach(Req *);
static void θcreate(Req *);
static void θdestroyfid(Fid *);
static void θend(Srv *);
static void θflush(Req *);
static void θopen(Req *);
static void θread(Req *);
static void θremove(Req *);
static void θstat(Req *);
static void θwalk(Req *);
static void θwstat(Req *);
static void θwrite(Req *);
static void mylistenproc(void *);
static void srvstarter(void *);
Srv θsrv = {
.attach = θattach,
.auth = auth9p,
.open = θopen,
.create = θcreate,
.read = θread,
.write = θwrite,
.remove = θremove,
.flush = θflush,
.stat = θstat,
.wstat = θwstat,
.walk = θwalk,
.destroyfid = θdestroyfid,
.start = θstart,
.end = θend,
};
static char *dev;
static char *laddr;
static Uglymap *uhd;
static Channel *lchan;
char *ddir, *dname;
uvlong starttime;
int doatimes;
int shutdown;
int mainstacksize = 16384;
static void
usage(void)
{
fprint(2, "Usage: %s [-anrsACD] [-m nblk] [-p port] device\n", argv0);
threadexits("usage");
}
void
threadmain(int argc, char *argv[])
{
Lmsg lmsg;
char *lstr, *p;
int doream, postcons, port, poststdin;
int doaoe, donfs, maxcache;
doream = 0;
postcons = 0;
poststdin = 0;
doaoe = 1;
donfs = 1;
maxcache = 4000;
port = 564;
ARGBEGIN {
case 'a':
doaoe = 0;
break;
case 'm':
maxcache = atoi(EARGF(usage()));
break;
case 'n':
donfs = 0;
break;
case 'p':
port = atoi(EARGF(usage()));
break;
case 'r':
doream = 1;
break;
case 's':
poststdin = 1;
break;
case 'A':
doatimes = 1;
break;
case 'C':
postcons = 1;
break;
case 'D':
++chatty9p;
break;
default:
usage();
} ARGEND
if(argc != 1)
usage();
dev = *argv;
lstr = smprint("tcp!*!%d", port);
starttime = nsec();
p = strrchr(dev, '/');
if(p == nil) {
ddir = ".";
dname = strdup(dev);
}
else {
ddir = mallocz(p - dev + 1, 1);
strncpy(ddir, dev, p - dev);
dname = strdup(p+1);
}
initcache(dev, maxcache);
if(doream)
ream(dev);
else
loadsuper();
inituid();
if(doaoe)
initaoe();
if(donfs)
initnfs();
lchan = chancreate(sizeof(Lmsg), 4);
laddr = lstr;
threadcreate(srvstarter, nil, 8192);
if(poststdin) {
lmsg.data = 1;
lmsg.rsys = estrdup9p("boot");
send(lchan, &lmsg);
postfd("θfs", 0);
}
/*
* Because the main in libthread runs the thread scheduler in
* the initial process, we can't daemonize in the usual way.
* The backgrounding is no big deal, but we want the parent
* to be able to wait until we're ready for an attach. So we
* don't do the console until almost the end and the parent
* can wait until θfsctl appears in /srv. It's not as elegant as
* letting the wait synchronize, but it's better than an arbitrary
* sleep.
*/
initcons(postcons);
proccreate(mylistenproc, nil, 8192);
}
void
halt9p(void)
{
Srvaux *sa;
Uglymap *u;
/* chanclose(lchan); */
for(u = uhd; u; u = u->next) {
close(u->s->infd);
close(u->s->outfd);
sa = u->s->aux;
closeioproc(sa->io9p);
}
}
static void
mysrvproc(void *a)
{
Srv *s;
int data;
s = a;
data = s->infd;
srv(s);
close(data);
threadexits(nil);
}
static void
srvstarter(void *)
{
Lmsg m;
Srv *s;
while(recv(lchan, &m)) {
if(shutdown)
break;
s = emalloc9p(sizeof(Srv));
*s = θsrv;
s->addr = m.rsys;
s->infd = s->outfd = m.data;
s->fpool = nil;
s->rpool = nil;
s->rbuf = nil;
s->wbuf = nil;
threadcreate(mysrvproc, s, 32 * 1024);
}
threadexits(nil);
}
static char*
getremotesys(char *ndir)
{
char buf[128], *serv, *sys;
int fd, n;
snprint(buf, sizeof buf, "%s/remote", ndir);
sys = nil;
fd = open(buf, OREAD);
if(fd >= 0) {
n = read(fd, buf, sizeof(buf)-1);
if(n>0) {
buf[n-1] = 0;
serv = strchr(buf, '!');
if(serv)
*serv = 0;
sys = estrdup9p(buf);
}
close(fd);
}
if(sys == nil)
sys = estrdup9p("unknown");
return sys;
}
static void
mylistenproc(void *)
{
Lmsg m;
char ndir[NETPATHLEN], dir[NETPATHLEN];
int ctl, data, nctl;
ctl = announce(laddr, dir);
if(ctl < 0) {
fprint(2, "%s: announce %s: %r", argv0, laddr);
return;
}
for(;;){
nctl = listen(dir, ndir);
if(nctl < 0){
fprint(2, "%s: listen %s: %r", argv0, laddr);
break;
}
data = accept(ctl, ndir);
if(data < 0){
fprint(2, "%s: accept %s: %r\n", argv0, ndir);
continue;
}
m.data = data;
m.rsys = getremotesys(ndir);
send(lchan, &m);
}
}
int
read9pmsg(int fd, void *abuf, uint n)
{
Srvaux *sa;
Uglymap *um;
Ioproc *io9p;
int m, len;
uchar *buf;
buf = abuf;
/*
* Grotesque, but this is research :)
*/
for(um = uhd; um && um->rbuf != buf; um = um->next) ;
if(um == nil) {
fprint(2, "no ugly mapping");
return 0;
}
sa = um->s->aux;
io9p = sa->io9p;
/* read count */
m = ioreadn(io9p, fd, buf, BIT32SZ);
if(m != BIT32SZ){
if(m < 0)
return -1;
return 0;
}
len = GBIT32(buf);
if(len <= BIT32SZ || len > n){
werrstr("bad length in 9P2000 message header");
return -1;
}
len -= BIT32SZ;
m = ioreadn(io9p, fd, buf+BIT32SZ, len);
if(m < len)
return 0;
return BIT32SZ+m;
}
static int
θhasperm(int fd, uvlong meta, char *uid, int p)
{
uvlong mode;
char *fuser, *fgroup;
int m;
if(allow)
return 1;
if(getmetaint(fd, meta, "mode", &mode) == MTnone)
return 1;
m = mode & 7; /* other */
if((p & m) == p)
return 1;
if((fuser = getmetastr(fd, meta, "uid")) != nil) {
if(strcmp(fuser, uid) == 0) {
m |= (mode>>6) & 7;
if((p & m) == p) {
free(fuser);
return 1;
}
}
free(fuser);
}
if((fgroup = getmetastr(fd, meta, "gid")) != nil) {
if(ingroup(uid, fgroup)) {
m |= (mode>>3) & 7;
if((p & m) == p) {
free(fgroup);
return 1;
}
}
free(fgroup);
}
return 0;
}
static void
attacher(void *a)
{
Req *r;
Fidaux *fa;
char *path;
uvlong rmeta, x;
r = a;
if(r->ifcall.aname == nil || strlen(r->ifcall.aname) == 0)
path = smprint("/");
else
path = smprint("/%s", r->ifcall.aname);
rmeta = q2m(-1, p2q(-1, path, 0), 0);
if(rmeta == 0)
respond(r, "no root");
else {
getmetaint(-1, rmeta, "qpath", &x);
r->fid->qid.path = x;
getmetaint(-1, rmeta, "qvers", &x);
r->fid->qid.vers = x;
getmetaint(-1, rmeta, "qtype", &x);
r->fid->qid.type = x;
r->ofcall.qid = r->fid->qid;
fa = malloc(sizeof(Fidaux));
r->fid->aux = fa;
fa->path = path;
fa->uname = estrdup9p(r->ifcall.uname);
fa->lsearch = 0;
fa->store = θmalloc(sizeof(Fdref));
incref(&fa->store->ref);
fa->store->fd = -1;
respond(r, nil);
}
threadexits(nil);
}
static void
θattach(Req *r)
{
if(authattach(r) < 0)
return;
threadcreate(attacher, r, 8192);
}
static void
_θcreate(void *a)
{
Req *r;
Qid nqid;
Fidaux *fa;
char *npath;
uvlong x;
// uvlong meta, pmeta, dirblk, now;
uvlong meta, pmeta, now;
r = a;
fa = r->fid->aux;
pmeta = q2m(-1, r->fid->qid.path, 0);
if(θhasperm(fa->store->fd, pmeta, fa->uname, AWRITE) == 0) {
respond(r, "permission denied");
threadexits(nil);
}
npath = smprint("%s/%s", fa->path, r->ifcall.name);
nqid.path = p2q(-1, npath, 1);
meta = q2m(-1, nqid.path, 1);
if(meta == 0) {
respond(r, "create failure");
free(npath);
threadexits(nil);
}
setmetastr(meta, "name", nil, r->ifcall.name, 0);
setmetaint(meta, "parent", nil, r->fid->qid.path);
nqid.vers = 0;
nqid.type = 0;
if(r->ifcall.perm & DMDIR)
nqid.type |= QTDIR;
if(r->ifcall.perm & DMAPPEND)
nqid.type |= QTAPPEND;
if(r->ifcall.perm & DMEXCL)
nqid.type |= QTEXCL;
if(r->ifcall.perm & DMTMP)
nqid.type |= QTTMP;
setmetaint(meta, "qpath", nil, nqid.path);
setmetaint(meta, "qvers", nil, nqid.vers);
setmetaint(meta, "qtype", nil, nqid.type);
setmetaint(meta, "mode", nil, r->ifcall.perm);
now = nsec();
setmetaint(meta, "atime", nil, now);
setmetaint(meta, "mtime", nil, now);
setmetaint(meta, "length", nil, 0);
setmetastr(meta, "uid", nil, fa->uname, 0);
setmetastr(meta, "gid", nil, fa->uname, 0);
setmetastr(meta, "muid", nil, fa->uname, 0);
if(getmetaint(-1, pmeta, "child", &x) == MTint)
setmetaint(meta, "sib", nil, x);
else
setmetaint(meta, "sib", nil, 0);
if(r->ifcall.perm & DMDIR)
setmetaint(meta, "child", nil, 0);
else
setmetaint(meta, "dblock", nil, 0);
setmetaint(pmeta, "child", nil, nqid.path);
if(getmetaint(-1, pmeta, "qvers", &x) != MTnone)
setmetaint(pmeta, "qvers", nil, x+1);
setmetaint(pmeta, "mtime", nil, now);
setmetastr(pmeta, "muid", nil, fa->uname, 0);
setqhash(nqid.path, meta);
free(fa->path);
fa->path = npath;
fa->lsearch = 0;
r->fid->qid = nqid;
r->ofcall.qid = nqid;
respond(r, nil);
savesuper();
threadexits(nil);
}
static void
θcreate(Req *r)
{
threadcreate(_θcreate, r, 8192);
}
static void
θdestroyfid(Fid *fid)
{
Fidaux *fa;
uvlong meta;
if(fid->qid.type & QTAUTH) {
authdestroy(fid);
return;
}
fa = fid->aux;
if(fid->omode != -1 && (fid->omode & ORCLOSE)) {
meta = q2m(fa->store->fd, fid->qid.path, 0);
if(meta != 0) {
freedata(meta);
rmdlist(meta, fid->qid.path);
rmq(fid->qid.path, meta);
rmmlist(meta);
if(fa)
rmp(fa->path);
}
}
if(fa == nil)
return;
if(fa->store && decref(&fa->store->ref) == 0) {
if(fa->store->fd != -1)
close(fa->store->fd);
free(fa->store);
}
free(fa->path);
free(fa->uname);
free(fa);
}
static void
θend(Srv *s)
{
Srvaux *sa;
Uglymap *um, *u;
resetmeta();
csync();
sa = s->aux;
if(sa) {
if(sa->io9p)
closeioproc(sa->io9p);
free(sa);
}
if(uhd == nil)
return;
if(uhd->s == s) {
um = uhd;
uhd = um->next;
free(um);
return;
}
for(um = uhd; um && um->next && um->next->s != s; um = um->next) ;
if(um && um->next) {
u = um->next;
um->next = u->next;
free(u);
}
}
static void
θflush(Req *r)
{
respond(r, nil);
}
static void
_θopen(void *a)
{
Fidaux *fa;
Req *r;
Fid *fid;
uvlong meta, x;
ulong need;
r = a;
fid = r->fid;
fa = fid->aux;
meta = q2m(fa->store->fd, fid->qid.path, 0);
if(meta == 0) {
respond(r, "no file");
threadexits(nil);
}
switch(r->ifcall.mode & 3) {
case OREAD:
need = AREAD;
break;
case OWRITE:
need = AWRITE;
break;
case ORDWR:
need = AREAD | AWRITE;
break;
case OEXEC:
need = AEXEC;
break;
default:
need = AREAD | AWRITE | AEXEC;
break;
}
if(r->ifcall.mode & OTRUNC)
need |= AWRITE;
if(θhasperm(fa->store->fd, meta, fa->uname, need) == 0) {
respond(r, "permission denied");
threadexits(nil);
}
if(r->ifcall.mode & ORCLOSE) {
/* check write permission on parent */
}
if(r->ifcall.mode & OTRUNC) {
setmetaint(meta, "length", nil, 0LL);
if(getmetaint(fa->store->fd, meta, "qvers", &x) != MTnone)
setmetaint(meta, "qvers", nil, x+1);
}
respond(r, nil);
threadexits(nil);
}
static void
θopen(Req *r)
{
threadcreate(_θopen, r, 8192);
}
static int
lzstat(int fd, uvlong meta, Dir *d)
{
uvlong x;
memset(&d->qid, 0, sizeof(Qid));
if(getmetaint(fd, meta, "qpath", &x) != MTnone)
d->qid.path = x;
if(getmetaint(fd, meta, "qvers", &x) != MTnone)
d->qid.vers = x;
if(getmetaint(fd, meta, "qtype", &x) != MTnone)
d->qid.type = x;
if(getmetaint(fd, meta, "mode", &x) != MTnone)
d->mode = x;
else
d->mode = 0;
if(getmetaint(fd, meta, "atime", &x) != MTnone)
d->atime = x / 1000000000;
else
d->atime = 0;
if(getmetaint(fd, meta, "mtime", &x) != MTnone)
d->mtime = x / 1000000000;
else
d->mtime = 0;
if(getmetaint(fd, meta, "length", &x) != MTnone)
d->length = x;
else
d->length = 0;
if((d->name = getmetastr(fd, meta, "name")) == nil) {
fprint(2, "where the streets have no name\n");
d->name = estrdup9p("<nil>");
}
/* If this is one of the roots, just call it '/' */
if(d->name[0] == '/')
d->name[1] = 0;
if((d->uid = getmetastr(fd, meta,"uid")) == nil)
d->uid = estrdup9p("none");
if((d->gid = getmetastr(fd, meta, "gid")) == nil)
d->gid = estrdup9p("none");
if((d->muid = getmetastr(fd, meta, "muid")) == nil)
d->muid = estrdup9p("none");
return 0;
}
static int
θgen(int n, Dir *dir, void *a)
{
Fidaux *fa;
Fid *fid;
uvlong meta, x;
int i;
fid = a;
fa = fid->aux;
if(n == fa->dirindex + 1 && fa->lsearch != 0) {
if(getmetaint(fa->store->fd, fa->lsearch, "sib", &x) == MTint)
meta = q2m(fa->store->fd, x, 0);
else {
meta = 0;
fprint(2, "no sibling in mblock %ulld\n", fa->lsearch);
}
}
else {
meta = q2m(fa->store->fd, fid->qid.path, 0);
if(meta == 0)
return -1;
if(getmetaint(fa->store->fd, meta, "child", &x) != MTint)
return -1;
meta = q2m(fa->store->fd, x, 0);
for(i = 0; i < n && meta != 0; ++i) {
getmetaint(fa->store->fd, meta, "sib", &x);
meta = q2m(fa->store->fd, x, 0);
}
}
fa->dirindex = n;
fa->lsearch = meta;
if(meta == 0)
return -1;
i = lzstat(fa->store->fd, meta, dir);
return i;
}
static void
_θread(void *a)
{
Fidaux *fa;
Req *r;
ulong tot;
r = a;
fa = r->fid->aux;
fa->lsearch = 0;
fa->dirindex = 0;
if(r->fid->qid.type & QTDIR) {
dirread9p(r, θgen, r->fid);
respond(r, nil);
threadexits(nil);
}
tot = θpread(fa->store->fd, r->fid->qid.path, r->ofcall.data, r->ifcall.count, r->ifcall.offset);
if(tot == -1) {
respond(r, "no metadata");
threadexits(nil);
}
r->ofcall.count = tot;
respond(r, nil);
threadexits(nil);
}
static void
θauthread(void *a)
{
Req *r;
r = a;
authread(r);
threadexits(nil);
}
static void
θread(Req *r)
{
if(r->fid->qid.type & QTAUTH) {
proccreate(θauthread, r, 8192);
return;
}
threadcreate(_θread, r, 8192);
}
static void
_θremove(void *a)
{
static QLock rlock;
Req *r;
Fidaux *fa;
uvlong meta, pmeta, qpath, now;
/*
* This lock is ugly. Its purpose is to serialize the removes so
* that we don't end up in the process of removing the same
* file more than once concurrently. It comes up when doing
* a mk clean on the kernel. I'm going to give some thought
* to better ways to handle this, but this should get around
* the issue for now.
*/
qlock(&rlock);
r = a;
fa = r->fid->aux;
meta = q2m(-1, r->fid->qid.path, 0);
if(meta == 0) {
qunlock(&rlock);
respond(r, nil);
threadexits(nil);
}
pmeta = 0;
/* check parent permission */
if(getmetaint(-1, meta, "parent", &qpath) != MTnone && qpath != 0) {
pmeta = q2m(-1, qpath, 0);
if(pmeta != 0) {
if(θhasperm(fa->store->fd, pmeta, fa->uname, AWRITE) == 0) {
qunlock(&rlock);
respond(r, "permission denied");
threadexits(nil);
}
}
}
if(r->fid->qid.type & QTDIR) {
if(getmetaint(-1, meta, "child", &qpath) != MTnone && qpath != 0) {
qunlock(&rlock);
respond(r, "not empty");
threadexits(nil);
}
}
now = nsec();
rmq(r->fid->qid.path, meta);
setmetaint(pmeta, "mtime", nil, now);
setmetastr(pmeta, "muid", nil, fa->uname, 0);
freedata(meta);
rmdlist(meta, r->fid->qid.path);
rmmlist(meta);
rmp(fa->path);
qunlock(&rlock);
respond(r, nil);
threadexits(nil);
}
static void
θremove(Req *r)
{
threadcreate(_θremove, r, 8192);
}
void
θstart(Srv *s)
{
Srvaux *sa;
Uglymap *um;
sa = malloc(sizeof(Srvaux));
sa->io9p = ioproc();
s->aux = sa;
um = malloc(sizeof(Uglymap));
um->s = s;
um->rbuf = s->rbuf;
um->next = uhd;
uhd = um;
}
static void
_θstat(void *a)
{
Req *r;
Fidaux *fa;
uvlong meta;
int n;
r = a;
fa = r->fid->aux;
meta = q2m(fa->store->fd, r->fid->qid.path, 0);
if(meta == 0)
respond(r, "no file");
else {
n = lzstat(fa->store->fd, meta, &r->d);
if(n == 0)
respond(r, nil);
else
respond(r, "errnt");
}
threadexits(nil);
}
static void
θstat(Req *r)
{
threadcreate(_θstat, r, 8192);
}
static char *
θwalk1(Fid *fid, char *name, void *)
{
Fidaux *fa;
char *npath, *sname, *spath;
uvlong meta, x;
int fd;
fa = (Fidaux *)(fid->aux);
npath = smprint("%s/%s", fa->path, name);
meta = q2m(fa->store->fd, p2q(fa->store->fd, npath, 0), 0);
if(meta == 0)
return "does not exit";
sname = getmetastr(fa->store->fd, meta, "snap");
if(sname == nil) {
free(fa->path);
fa->path = npath;
}
else {
free(npath);
spath = smprint("%s/%s", ddir, sname);
free(sname);
fd = open(spath, OREAD);
if(fd < 0)
return "snap open";
free(fa->path);
fa->path = estrdup9p("/");
if(decref(&fa->store->ref) == 0) {
if(fa->store->fd != -1)
close(fa->store->fd);
free(fa->store);
}
fa->store = θmalloc(sizeof(Fdref));
incref(&fa->store->ref);
fa->store->fd = fd;
meta = q2m(fa->store->fd, p2q(fa->store->fd, "/", 0), 0);
if(meta == 0)
return "no root";
}
if(getmetaint(fa->store->fd, meta, "qpath", &x) != MTint)
return "no qid";
fid->qid.path = x;
getmetaint(fa->store->fd, meta, "qvers", &x);
fid->qid.vers = x;
getmetaint(fa->store->fd, meta, "qtype", &x);
fid->qid.type = x;
return nil;
}
static char *
θclone(Fid *oldfid, Fid *newfid, void *)
{
Fidaux *ofa, *nfa;
ofa = (Fidaux *)(oldfid->aux);
nfa = newfid->aux = θmalloc(sizeof(Fidaux));
*nfa = *ofa;
nfa->path = estrdup9p(ofa->path);
nfa->uname = estrdup9p(ofa->uname);
incref(&nfa->store->ref);
return nil;
}
static void
_θwalk(void *a)
{
Req *r;
Fdref *store;
Fidaux *fa;
char *npath, *p, *e;
uvlong qp, meta, x;
int nlen;
int i, fd;
r = a;
fa = r->fid->aux;
store = fa->store;
fd = store->fd;
if(r->ifcall.nwname == 1 && strcmp(r->ifcall.wname[0], "..") == 0) {
npath = estrdup9p(fa->path);
p = strrchr(npath, '/');
if(p && p != npath)
*p = 0;
}
else {
nlen = strlen(fa->path);
for(i = 0; i < r->ifcall.nwname; ++i)
nlen += strlen(r->ifcall.wname[i]) + 1;
npath = θmalloc(nlen + 1);
p = npath;
e = npath + nlen + 1;
p = seprint(p, e, "%s", fa->path);
for(i = 0; i < r->ifcall.nwname; ++i)
p = seprint(p, e, "/%s", r->ifcall.wname[i]);
}
/*
* If we can get there directly, do it, otherwise, fall
* back to the one step at a time using walkandclone
*/
meta = q2m(fd, p2q(fd, npath, 0), 0);
if(meta == 0) {
walkandclone(r, θwalk1, θclone, nil);
free(npath);
threadexits(nil);
}
if(p = getmetastr(fd, meta, "snap")) {
free(p);
walkandclone(r, θwalk1, θclone, nil);
free(npath);
threadexits(nil);
}
fa = r->newfid->aux;
if(r->fid == r->newfid)
free(fa->path);
else {
fa = r->newfid->aux = θmalloc(sizeof(Fidaux));
fa->uname = estrdup9p(((Fidaux *)(r->fid->aux))->uname);
fa->store = store;
incref(&store->ref);
}
fa->path = npath;
if(r->ifcall.nwname == 0) {
respond(r, nil);
threadexits(nil);
}
r->ofcall.nwqid = r->ifcall.nwname;
for(i = r->ifcall.nwname - 1; i >= 0; --i) {
if(getmetaint(fd, meta, "qpath", &x) == MTnone) {
respond(r, "errnt");
threadexits(nil);
}
r->ofcall.wqid[i].path = x;
getmetaint(fd, meta, "qvers", &x);
r->ofcall.wqid[i].vers = x;
getmetaint(fd, meta, "qtype", &x);
r->ofcall.wqid[i].type = x;
getmetaint(fd, meta, "parent", &qp);
meta = q2m(fd, qp, 0);
}
respond(r, nil);
threadexits(nil);
}
static void
θwalk(Req *r)
{
threadcreate(_θwalk, r, 8192);
}
static void
_θwrite(void *a)
{
Req *r;
ulong tot;
r = a;
if(r->fid->qid.type & QTAPPEND)
tot = θpwrite(r->fid->qid.path, r->ifcall.data, r->ifcall.count, 0, 2);
else
tot = θpwrite(r->fid->qid.path, r->ifcall.data, r->ifcall.count, r->ifcall.offset, 1);
if(tot == -1) {
respond(r, "no metadata");
threadexits(nil);
}
r->ofcall.count = tot;
respond(r, nil);
threadexits(nil);
}
static void
θauthwrite(void *a)
{
Req *r;
r = a;
authwrite(r);
threadexits(nil);
}
static void
θwrite(Req *r)
{
if(r->fid->qid.type & QTAUTH) {
proccreate(θauthwrite, r, 8192);
return;
}
threadcreate(_θwrite, r, 8192);
}
static void
_θwstat(void *a)
{
Req *r;
Fidaux *fa;
Qid nqid;
char *p, *gid, *uid, *newpath;
uvlong meta, pmeta, x, pqpath;
r = a;
fa = r->fid->aux;
meta = q2m(-1, r->fid->qid.path, 0);
if(meta == 0) {
respond(r, "no metadata");
threadexits(nil);
}
p = strrchr(fa->path, '/');
if(p && fa->path)
newpath = smprint("%.*s/%s", (int)(p - fa->path), fa->path, r->d.name);
else
newpath = estrdup9p(r->d.name);
if(allow)
goto skipperm;
uid = getmetastr(-1, meta, "uid");
gid = getmetastr(-1, meta, "gid");
/* Becuase wstat is defined to be all or none, first check all the permissions */
if(strlen(r->d.name) > 0) {
if(getmetaint(-1, meta, "parent", &pqpath) != MTnone && pqpath != 0) {
pmeta = q2m(-1, pqpath, 0);
if(pmeta != 0) {
if(θhasperm(-1, pmeta, fa->uname, AWRITE) == 0) {
free(newpath);
free(gid);
free(uid);
respond(r, "permission denied");
threadexits(nil);
}
}
}
if(q2m(-1, p2q(-1, newpath, 0), 0) != 0) {
free(gid);
free(uid);
respond(r, "file extists");
threadexits(nil);
}
}
if(r->d.length != 0xffffffffffffffffLL) {
if((r->fid->qid.type & QTDIR) && r->d.length != 0) {
free(newpath);
free(gid);
free(uid);
respond(r, "non-zero size on directory");
threadexits(nil);
}
if(θhasperm(-1, meta, fa->uname, AWRITE) == 0) {
free(newpath);
free(gid);
free(uid);
respond(r, "permission denied");
threadexits(nil);
}
}
if(r->d.mode != 0xffffffff || r->d.mtime != 0xffffffff) {
if(!(strcmp(fa->uname, uid) == 0 || isleader(fa->uname, gid))) {
free(gid);
free(uid);
free(newpath);
respond(r, "not owner");
threadexits(nil);
}
}
if(strlen(r->d.gid) > 0) {
if(!(strcmp(fa->uname, uid) == 0 && ingroup(fa->uname, gid) || isleader(fa->uname, gid))) {
free(gid);
free(newpath);
respond(r, "not owner");
threadexits(nil);
}
}
free(gid);
free(uid);
skipperm:
/* Now the we know we have permission, make all the changes */
if(r->d.mode != 0xffffffff) {
getmetaint(-1, meta, "qpath", &x);
nqid.path = x;
getmetaint(-1, meta, "qvers", &x);
nqid.vers = x;
getmetaint(-1, meta, "qtype", &x);
nqid.type = x;
x = nqid.type & QTDIR;
if(r->d.mode & DMAPPEND)
x |= QTAPPEND;
if(r->d.mode & DMEXCL)
x |= QTEXCL;
if(r->d.mode & DMTMP)
x |= QTTMP;
if(x != nqid.type)
setmetaint(meta, "qtype", nil, x);
setmetaint(meta, "mode", nil, r->d.mode);
if(getmetaint(-1, meta, "unixmode", &x) != MTnone)
setmetaint(meta, "unixmode", nil, x & ~0777 | r->d.mode & 0777);
}
if(r->d.mtime != 0xffffffff)
setmetaint(meta, "mtime", nil, r->d.mtime * 1000000000LL);
if(r->d.length != 0xffffffffffffffffLL)
setmetaint(meta, "length", nil, r->d.length);
if(strlen(r->d.name) > 0) {
setmetastr(meta, "name", nil, r->d.name, 0);
rehashpath(r->fid->qid.path, fa->path, newpath);
free(fa->path);
fa->path = newpath;
}
if(allow && strlen(r->d.uid) > 0)
setmetastr(meta, "uid", nil, r->d.uid, 0);
if(strlen(r->d.gid) > 0)
setmetastr(meta, "gid", nil, r->d.gid, 0);
respond(r, nil);
threadexits(nil);
}
static void
θwstat(Req *r)
{
threadcreate(_θwstat, r, 8192);
}
|