plan9port/src/cmd/vac/vacfs.c
David du Colombier 3a616eaa80 vacfs: fix create srv with ORCLOSE on plan 9
In the current code, the srv file is removed
just after the main thread exits, while the
srv thread is still running, which is not
the expected behavior.
We moved the srv creation just before the
procrfork, in order that the srv file will
not be removed until the srv thread exits.

R=rsc
http://codereview.appspot.com/6397047
2012-08-03 21:12:10 +02:00

864 lines
14 KiB
C

#include "stdinc.h"
#include <fcall.h>
#include "vac.h"
typedef struct Fid Fid;
enum
{
OPERM = 0x3 /* mask of all permission types in open mode */
};
struct Fid
{
short busy;
short open;
int fid;
char *user;
Qid qid;
VacFile *file;
VacDirEnum *vde;
Fid *next;
};
enum
{
Pexec = 1,
Pwrite = 2,
Pread = 4,
Pother = 1,
Pgroup = 8,
Powner = 64
};
Fid *fids;
uchar *data;
int mfd[2];
int srvfd = -1;
char *user;
uchar mdata[8192+IOHDRSZ];
int messagesize = sizeof mdata;
Fcall rhdr;
Fcall thdr;
VacFs *fs;
VtConn *conn;
int noperm;
char *defmnt;
Fid * newfid(int);
void error(char*);
void io(void);
void vacshutdown(void);
void usage(void);
int perm(Fid*, int);
int permf(VacFile*, char*, int);
ulong getl(void *p);
void init(char*, char*, long, int);
int vacdirread(Fid *f, char *p, long off, long cnt);
int vacstat(VacFile *parent, VacDir *vd, uchar *p, int np);
void srv(void* a);
char *rflush(Fid*), *rversion(Fid*),
*rauth(Fid*), *rattach(Fid*), *rwalk(Fid*),
*ropen(Fid*), *rcreate(Fid*),
*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
char *(*fcalls[Tmax])(Fid*);
void
initfcalls(void)
{
fcalls[Tflush]= rflush;
fcalls[Tversion]= rversion;
fcalls[Tattach]= rattach;
fcalls[Tauth]= rauth;
fcalls[Twalk]= rwalk;
fcalls[Topen]= ropen;
fcalls[Tcreate]= rcreate;
fcalls[Tread]= rread;
fcalls[Twrite]= rwrite;
fcalls[Tclunk]= rclunk;
fcalls[Tremove]= rremove;
fcalls[Tstat]= rstat;
fcalls[Twstat]= rwstat;
}
char Eperm[] = "permission denied";
char Enotdir[] = "not a directory";
char Enotexist[] = "file does not exist";
char Einuse[] = "file in use";
char Eexist[] = "file exists";
char Enotowner[] = "not owner";
char Eisopen[] = "file already open for I/O";
char Excl[] = "exclusive use file already open";
char Ename[] = "illegal name";
char Erdonly[] = "read only file system";
char Eio[] = "i/o error";
char Eempty[] = "directory is not empty";
char Emode[] = "illegal mode";
int dflag;
void
notifyf(void *a, char *s)
{
USED(a);
if(strncmp(s, "interrupt", 9) == 0)
noted(NCONT);
noted(NDFLT);
}
#define TWID64 ~(u64int)0
static u64int
unittoull(char *s)
{
char *es;
u64int n;
if(s == nil)
return TWID64;
n = strtoul(s, &es, 0);
if(*es == 'k' || *es == 'K'){
n *= 1024;
es++;
}else if(*es == 'm' || *es == 'M'){
n *= 1024*1024;
es++;
}else if(*es == 'g' || *es == 'G'){
n *= 1024*1024*1024;
es++;
}
if(*es != '\0')
return TWID64;
return n;
}
void
threadmain(int argc, char *argv[])
{
char *defsrv, *srvname;
int p[2], fd;
int stdio;
char *host = nil;
ulong mem;
mem = 16<<20;
stdio = 0;
fmtinstall('H', encodefmt);
fmtinstall('V', vtscorefmt);
fmtinstall('F', vtfcallfmt);
defmnt = nil;
defsrv = nil;
ARGBEGIN{
case 'd':
fmtinstall('F', fcallfmt);
dflag = 1;
break;
case 'i':
defmnt = nil;
stdio = 1;
mfd[0] = 0;
mfd[1] = 1;
break;
case 'h':
host = EARGF(usage());
break;
case 'S':
defsrv = EARGF(usage());
break;
case 's':
defsrv = "vacfs";
break;
case 'M':
mem = unittoull(EARGF(usage()));
break;
case 'm':
defmnt = EARGF(usage());
break;
case 'p':
noperm = 1;
break;
case 'V':
chattyventi = 1;
break;
default:
usage();
}ARGEND
if(argc != 1)
usage();
#ifdef PLAN9PORT
if(defsrv == nil && defmnt == nil && !stdio){
srvname = strchr(argv[0], '/');
if(srvname)
srvname++;
else
srvname = argv[0];
defsrv = vtmalloc(6+strlen(srvname)+1);
strcpy(defsrv, "vacfs.");
strcat(defsrv, srvname);
if(strcmp(defsrv+strlen(defsrv)-4, ".vac") == 0)
defsrv[strlen(defsrv)-4] = 0;
}
#else
if(defsrv == nil && defmnt == nil && !stdio)
defmnt = "/n/vac";
#endif
if(stdio && defmnt)
sysfatal("cannot use -m with -i");
initfcalls();
notify(notifyf);
user = getuser();
conn = vtdial(host);
if(conn == nil)
sysfatal("could not connect to server: %r");
if(vtconnect(conn) < 0)
sysfatal("vtconnect: %r");
fs = vacfsopen(conn, argv[0], VtOREAD, mem);
if(fs == nil)
sysfatal("vacfsopen: %r");
if(!stdio){
if(pipe(p) < 0)
sysfatal("pipe failed: %r");
mfd[0] = p[0];
mfd[1] = p[0];
srvfd = p[1];
#ifndef PLAN9PORT
if(defsrv){
srvname = smprint("/srv/%s", defsrv);
fd = create(srvname, OWRITE|ORCLOSE, 0666);
if(fd < 0)
sysfatal("create %s: %r", srvname);
if(fprint(fd, "%d", srvfd) < 0)
sysfatal("write %s: %r", srvname);
free(srvname);
}
#endif
}
#ifdef PLAN9PORT
USED(fd);
proccreate(srv, 0, 32 * 1024);
if(!stdio && post9pservice(p[1], defsrv, defmnt) < 0)
sysfatal("post9pservice");
#else
procrfork(srv, 0, 32 * 1024, RFFDG|RFNAMEG|RFNOTEG);
if(!stdio){
close(p[0]);
if(defmnt){
if(mount(srvfd, -1, defmnt, MREPL|MCREATE, "") < 0)
sysfatal("mount %s: %r", defmnt);
}
}
#endif
threadexits(0);
}
void
srv(void *a)
{
USED(a);
io();
vacshutdown();
}
void
usage(void)
{
fprint(2, "usage: %s [-sd] [-h host] [-m mountpoint] [-M mem] vacfile\n", argv0);
threadexitsall("usage");
}
char*
rversion(Fid *unused)
{
Fid *f;
USED(unused);
for(f = fids; f; f = f->next)
if(f->busy)
rclunk(f);
if(rhdr.msize < 256)
return vtstrdup("version: message size too small");
messagesize = rhdr.msize;
if(messagesize > sizeof mdata)
messagesize = sizeof mdata;
thdr.msize = messagesize;
if(strncmp(rhdr.version, "9P2000", 6) != 0)
return vtstrdup("unrecognized 9P version");
thdr.version = "9P2000";
return nil;
}
char*
rflush(Fid *f)
{
USED(f);
return 0;
}
char*
rauth(Fid *f)
{
USED(f);
return vtstrdup("vacfs: authentication not required");
}
char*
rattach(Fid *f)
{
/* no authentication for the momment */
VacFile *file;
char err[80];
file = vacfsgetroot(fs);
if(file == nil) {
rerrstr(err, sizeof err);
return vtstrdup(err);
}
f->busy = 1;
f->file = file;
f->qid.path = vacfilegetid(f->file);
f->qid.vers = 0;
f->qid.type = QTDIR;
thdr.qid = f->qid;
if(rhdr.uname[0])
f->user = vtstrdup(rhdr.uname);
else
f->user = "none";
return 0;
}
char*
rwalk(Fid *f)
{
VacFile *file, *nfile;
Fid *nf;
int nqid, nwname;
Qid qid;
char *err = nil;
if(f->busy == 0)
return Enotexist;
nf = nil;
if(rhdr.fid != rhdr.newfid){
if(f->open)
return vtstrdup(Eisopen);
if(f->busy == 0)
return vtstrdup(Enotexist);
nf = newfid(rhdr.newfid);
if(nf->busy)
return vtstrdup(Eisopen);
nf->busy = 1;
nf->open = 0;
nf->qid = f->qid;
nf->file = vacfileincref(f->file);
nf->user = vtstrdup(f->user);
f = nf;
}
nwname = rhdr.nwname;
/* easy case */
if(nwname == 0) {
thdr.nwqid = 0;
return 0;
}
file = f->file;
vacfileincref(file);
qid = f->qid;
for(nqid = 0; nqid < nwname; nqid++){
if((qid.type & QTDIR) == 0){
err = Enotdir;
break;
}
if(!permf(file, f->user, Pexec)) {
err = Eperm;
break;
}
nfile = vacfilewalk(file, rhdr.wname[nqid]);
if(nfile == nil)
break;
vacfiledecref(file);
file = nfile;
qid.type = QTFILE;
if(vacfileisdir(file))
qid.type = QTDIR;
#ifdef PLAN9PORT
if(vacfilegetmode(file)&ModeLink)
qid.type = QTSYMLINK;
#endif
qid.vers = vacfilegetmcount(file);
qid.path = vacfilegetid(file);
thdr.wqid[nqid] = qid;
}
thdr.nwqid = nqid;
if(nqid == nwname){
/* success */
f->qid = thdr.wqid[nqid-1];
vacfiledecref(f->file);
f->file = file;
return 0;
}
vacfiledecref(file);
if(nf != nil)
rclunk(nf);
/* only error on the first element */
if(nqid == 0)
return vtstrdup(err);
return 0;
}
char *
ropen(Fid *f)
{
int mode, trunc;
if(f->open)
return vtstrdup(Eisopen);
if(!f->busy)
return vtstrdup(Enotexist);
mode = rhdr.mode;
thdr.iounit = messagesize - IOHDRSZ;
if(f->qid.type & QTDIR){
if(mode != OREAD)
return vtstrdup(Eperm);
if(!perm(f, Pread))
return vtstrdup(Eperm);
thdr.qid = f->qid;
f->vde = nil;
f->open = 1;
return 0;
}
if(mode & ORCLOSE)
return vtstrdup(Erdonly);
trunc = mode & OTRUNC;
mode &= OPERM;
if(mode==OWRITE || mode==ORDWR || trunc)
if(!perm(f, Pwrite))
return vtstrdup(Eperm);
if(mode==OREAD || mode==ORDWR)
if(!perm(f, Pread))
return vtstrdup(Eperm);
if(mode==OEXEC)
if(!perm(f, Pexec))
return vtstrdup(Eperm);
thdr.qid = f->qid;
thdr.iounit = messagesize - IOHDRSZ;
f->open = 1;
return 0;
}
char*
rcreate(Fid* fid)
{
VacFile *vf;
ulong mode;
if(fid->open)
return vtstrdup(Eisopen);
if(!fid->busy)
return vtstrdup(Enotexist);
if(fs->mode & ModeSnapshot)
return vtstrdup(Erdonly);
vf = fid->file;
if(!vacfileisdir(vf))
return vtstrdup(Enotdir);
if(!permf(vf, fid->user, Pwrite))
return vtstrdup(Eperm);
mode = rhdr.perm & 0777;
if(rhdr.perm & DMDIR){
if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))
return vtstrdup(Emode);
switch(rhdr.mode & OPERM){
default:
return vtstrdup(Emode);
case OEXEC:
case OREAD:
break;
case OWRITE:
case ORDWR:
return vtstrdup(Eperm);
}
mode |= ModeDir;
}
vf = vacfilecreate(vf, rhdr.name, mode);
if(vf == nil) {
char err[80];
rerrstr(err, sizeof err);
return vtstrdup(err);
}
vacfiledecref(fid->file);
fid->file = vf;
fid->qid.type = QTFILE;
if(vacfileisdir(vf))
fid->qid.type = QTDIR;
fid->qid.vers = vacfilegetmcount(vf);
fid->qid.path = vacfilegetid(vf);
thdr.qid = fid->qid;
thdr.iounit = messagesize - IOHDRSZ;
return 0;
}
char*
rread(Fid *f)
{
char *buf;
vlong off;
int cnt;
VacFile *vf;
char err[80];
int n;
if(!f->busy)
return vtstrdup(Enotexist);
vf = f->file;
thdr.count = 0;
off = rhdr.offset;
buf = thdr.data;
cnt = rhdr.count;
if(f->qid.type & QTDIR)
n = vacdirread(f, buf, off, cnt);
else if(vacfilegetmode(f->file)&ModeDevice)
return vtstrdup("device");
else if(vacfilegetmode(f->file)&ModeLink)
return vtstrdup("symbolic link");
else if(vacfilegetmode(f->file)&ModeNamedPipe)
return vtstrdup("named pipe");
else
n = vacfileread(vf, buf, cnt, off);
if(n < 0) {
rerrstr(err, sizeof err);
return vtstrdup(err);
}
thdr.count = n;
return 0;
}
char*
rwrite(Fid *f)
{
USED(f);
return vtstrdup(Erdonly);
}
char *
rclunk(Fid *f)
{
f->busy = 0;
f->open = 0;
vtfree(f->user);
f->user = nil;
if(f->file)
vacfiledecref(f->file);
f->file = nil;
vdeclose(f->vde);
f->vde = nil;
return 0;
}
char *
rremove(Fid *f)
{
VacFile *vf, *vfp;
char errbuf[80];
char *err = nil;
if(!f->busy)
return vtstrdup(Enotexist);
vf = f->file;
vfp = vacfilegetparent(vf);
if(!permf(vfp, f->user, Pwrite)) {
err = Eperm;
goto Exit;
}
if(!vacfileremove(vf)) {
rerrstr(errbuf, sizeof errbuf);
err = errbuf;
}
Exit:
vacfiledecref(vfp);
rclunk(f);
return vtstrdup(err);
}
char *
rstat(Fid *f)
{
VacDir dir;
static uchar statbuf[1024];
VacFile *parent;
if(!f->busy)
return vtstrdup(Enotexist);
parent = vacfilegetparent(f->file);
vacfilegetdir(f->file, &dir);
thdr.stat = statbuf;
thdr.nstat = vacstat(parent, &dir, thdr.stat, sizeof statbuf);
vdcleanup(&dir);
vacfiledecref(parent);
return 0;
}
char *
rwstat(Fid *f)
{
if(!f->busy)
return vtstrdup(Enotexist);
return vtstrdup(Erdonly);
}
int
vacstat(VacFile *parent, VacDir *vd, uchar *p, int np)
{
int ret;
Dir dir;
#ifdef PLAN9PORT
int n;
VacFile *vf;
uvlong size;
char *ext = nil;
#endif
memset(&dir, 0, sizeof(dir));
dir.qid.path = vd->qid + vacfilegetqidoffset(parent);
if(vd->qidspace)
dir.qid.path += vd->qidoffset;
dir.qid.vers = vd->mcount;
dir.mode = vd->mode & 0777;
if(vd->mode & ModeAppend){
dir.qid.type |= QTAPPEND;
dir.mode |= DMAPPEND;
}
if(vd->mode & ModeExclusive){
dir.qid.type |= QTEXCL;
dir.mode |= DMEXCL;
}
if(vd->mode & ModeDir){
dir.qid.type |= QTDIR;
dir.mode |= DMDIR;
}
#ifdef PLAN9PORT
if(vd->mode & (ModeLink|ModeDevice|ModeNamedPipe)){
vf = vacfilewalk(parent, vd->elem);
if(vf == nil)
return 0;
vacfilegetsize(vf, &size);
ext = malloc(size+1);
if(ext == nil)
return 0;
n = vacfileread(vf, ext, size, 0);
ext[size] = 0;
vacfiledecref(vf);
if(vd->mode & ModeLink){
dir.qid.type |= QTSYMLINK;
dir.mode |= DMSYMLINK;
}
if(vd->mode & ModeDevice)
dir.mode |= DMDEVICE;
if(vd->mode & ModeNamedPipe)
dir.mode |= DMNAMEDPIPE;
}
#endif
dir.atime = vd->atime;
dir.mtime = vd->mtime;
dir.length = vd->size;
dir.name = vd->elem;
dir.uid = vd->uid;
dir.gid = vd->gid;
dir.muid = vd->mid;
ret = convD2M(&dir, p, np);
#ifdef PLAN9PORT
free(ext);
#endif
return ret;
}
int
vacdirread(Fid *f, char *p, long off, long cnt)
{
int i, n, nb;
VacDir vd;
/*
* special case of rewinding a directory
* otherwise ignore the offset
*/
if(off == 0 && f->vde){
vdeclose(f->vde);
f->vde = nil;
}
if(f->vde == nil){
f->vde = vdeopen(f->file);
if(f->vde == nil)
return -1;
}
for(nb = 0; nb < cnt; nb += n) {
i = vderead(f->vde, &vd);
if(i < 0)
return -1;
if(i == 0)
break;
n = vacstat(f->file, &vd, (uchar*)p, cnt-nb);
if(n <= BIT16SZ) {
vdeunread(f->vde);
break;
}
vdcleanup(&vd);
p += n;
}
return nb;
}
Fid *
newfid(int fid)
{
Fid *f, *ff;
ff = 0;
for(f = fids; f; f = f->next)
if(f->fid == fid)
return f;
else if(!ff && !f->busy)
ff = f;
if(ff){
ff->fid = fid;
return ff;
}
f = vtmallocz(sizeof *f);
f->fid = fid;
f->next = fids;
fids = f;
return f;
}
void
io(void)
{
char *err;
int n;
for(;;){
n = read9pmsg(mfd[0], mdata, sizeof mdata);
if(n <= 0)
break;
if(convM2S(mdata, n, &rhdr) != n)
sysfatal("convM2S conversion error");
if(dflag)
fprint(2, "vacfs:<-%F\n", &rhdr);
thdr.data = (char*)mdata + IOHDRSZ;
if(!fcalls[rhdr.type])
err = "bad fcall type";
else
err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
if(err){
thdr.type = Rerror;
thdr.ename = err;
#ifdef PLAN9PORT
thdr.errornum = 0;
#endif
}else{
thdr.type = rhdr.type + 1;
thdr.fid = rhdr.fid;
}
thdr.tag = rhdr.tag;
if(dflag)
fprint(2, "vacfs:->%F\n", &thdr);
n = convS2M(&thdr, mdata, messagesize);
if(n <= BIT16SZ)
sysfatal("convS2M conversion error");
if(err)
vtfree(err);
if(write(mfd[1], mdata, n) != n)
sysfatal("mount write: %r");
}
}
int
permf(VacFile *vf, char *user, int p)
{
VacDir dir;
ulong perm;
if(vacfilegetdir(vf, &dir))
return 0;
perm = dir.mode & 0777;
if(noperm)
goto Good;
if((p*Pother) & perm)
goto Good;
if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm))
goto Good;
if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm))
goto Good;
vdcleanup(&dir);
return 0;
Good:
vdcleanup(&dir);
return 1;
}
int
perm(Fid *f, int p)
{
return permf(f->file, f->user, p);
}
void
vacshutdown(void)
{
Fid *f;
for(f = fids; f; f = f->next) {
if(!f->busy)
continue;
rclunk(f);
}
vacfsclose(fs);
vthangup(conn);
}