aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/vac/vacfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/vac/vacfs.c')
-rw-r--r--src/cmd/vac/vacfs.c849
1 files changed, 849 insertions, 0 deletions
diff --git a/src/cmd/vac/vacfs.c b/src/cmd/vac/vacfs.c
new file mode 100644
index 00000000..7702b0dd
--- /dev/null
+++ b/src/cmd/vac/vacfs.c
@@ -0,0 +1,849 @@
+#include "stdinc.h"
+#include <auth.h>
+#include <fcall.h>
+#include "vac.h"
+
+typedef struct Fid Fid;
+typedef struct DirBuf DirBuf;
+
+enum
+{
+ OPERM = 0x3, /* mask of all permission types in open mode */
+};
+
+enum
+{
+ DirBufSize = 20,
+};
+
+struct Fid
+{
+ short busy;
+ short open;
+ int fid;
+ char *user;
+ Qid qid;
+ VacFile *file;
+
+ DirBuf *db;
+
+ Fid *next;
+};
+
+struct DirBuf
+{
+ VacDirEnum *vde;
+ VacDir buf[DirBufSize];
+ int i, n;
+ int eof;
+};
+
+enum
+{
+ Pexec = 1,
+ Pwrite = 2,
+ Pread = 4,
+ Pother = 1,
+ Pgroup = 8,
+ Powner = 64,
+};
+
+Fid *fids;
+uchar *data;
+int mfd[2];
+char *user;
+uchar mdata[8192+IOHDRSZ];
+int messagesize = sizeof mdata;
+Fcall rhdr;
+Fcall thdr;
+VacFS *fs;
+VtSession *session;
+int noperm;
+
+Fid * newfid(int);
+void error(char*);
+void io(void);
+void shutdown(void);
+void usage(void);
+int perm(Fid*, int);
+int permf(VacFile*, char*, int);
+ulong getl(void *p);
+void init(char*, char*, long, int);
+DirBuf *dirBufAlloc(VacFile*);
+VacDir *dirBufGet(DirBuf*);
+int dirBufUnget(DirBuf*);
+void dirBufFree(DirBuf*);
+int vacdirread(Fid *f, char *p, long off, long cnt);
+int vdStat(VacDir *vd, uchar *p, int np);
+
+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[])(Fid*) = {
+ [Tflush] rflush,
+ [Tversion] rversion,
+ [Tattach] rattach,
+ [Tauth] rauth,
+ [Twalk] rwalk,
+ [Topen] ropen,
+ [Tcreate] rcreate,
+ [Tread] rread,
+ [Twrite] rwrite,
+ [Tclunk] rclunk,
+ [Tremove] rremove,
+ [Tstat] rstat,
+ [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);
+}
+
+void
+main(int argc, char *argv[])
+{
+ char *defmnt;
+ int p[2];
+ char buf[12];
+ int fd;
+ int stdio = 0;
+ char *host = nil;
+ long ncache = 1000;
+ int readOnly = 1;
+
+ defmnt = "/n/vac";
+ ARGBEGIN{
+ case 'd':
+ fmtinstall('F', fcallfmt);
+ dflag = 1;
+ break;
+ case 'c':
+ ncache = atoi(ARGF());
+ break;
+ case 'i':
+ defmnt = 0;
+ stdio = 1;
+ mfd[0] = 0;
+ mfd[1] = 1;
+ break;
+ case 'h':
+ host = ARGF();
+ break;
+ case 's':
+ defmnt = 0;
+ break;
+ case 'p':
+ noperm = 1;
+ break;
+ case 'm':
+ defmnt = ARGF();
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc != 1)
+ usage();
+
+ vtAttach();
+
+ init(argv[0], host, ncache, readOnly);
+
+ if(pipe(p) < 0)
+ sysfatal("pipe failed: %r");
+ if(!stdio){
+ mfd[0] = p[0];
+ mfd[1] = p[0];
+ if(defmnt == 0){
+ fd = create("#s/vacfs", OWRITE, 0666);
+ if(fd < 0)
+ sysfatal("create of /srv/vacfs failed: %r");
+ sprint(buf, "%d", p[1]);
+ if(write(fd, buf, strlen(buf)) < 0)
+ sysfatal("writing /srv/vacfs: %r");
+ }
+ }
+
+ switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
+ case -1:
+ sysfatal("fork: %r");
+ case 0:
+ vtAttach();
+ close(p[1]);
+ io();
+ shutdown();
+ break;
+ default:
+ close(p[0]); /* don't deadlock if child fails */
+ if(defmnt && mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0)
+ sysfatal("mount failed: %r");
+ }
+ vtDetach();
+ exits(0);
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-sd] [-h host] [-c ncache] [-m mountpoint] vacfile\n", argv0);
+ exits("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 "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 "unrecognized 9P version";
+ thdr.version = "9P2000";
+ return nil;
+}
+
+char*
+rflush(Fid *f)
+{
+ USED(f);
+ return 0;
+}
+
+char*
+rauth(Fid *f)
+{
+ USED(f);
+ return "vacfs: authentication not required";
+}
+
+char*
+rattach(Fid *f)
+{
+ /* no authentication for the momment */
+ VacFile *file;
+
+ file = vfsGetRoot(fs);
+ if(file == nil)
+ return vtGetError();
+ f->busy = 1;
+ f->file = file;
+ f->qid = (Qid){vfGetId(f->file), 0, QTDIR};
+ thdr.qid = f->qid;
+ if(rhdr.uname[0])
+ f->user = vtStrDup(rhdr.uname);
+ else
+ f->user = "none";
+ return 0;
+}
+
+VacFile*
+_vfWalk(VacFile *file, char *name)
+{
+ VacFile *n;
+
+ n = vfWalk(file, name);
+ if(n)
+ return n;
+ if(strcmp(name, "SLASH") == 0)
+ return vfWalk(file, "/");
+ return nil;
+}
+
+char*
+rwalk(Fid *f)
+{
+ VacFile *file, *nfile;
+ Fid *nf;
+ int nqid, nwname;
+ Qid qid;
+
+ if(f->busy == 0)
+ return Enotexist;
+ nf = nil;
+ if(rhdr.fid != rhdr.newfid){
+ if(f->open)
+ return Eisopen;
+ if(f->busy == 0)
+ return Enotexist;
+ nf = newfid(rhdr.newfid);
+ if(nf->busy)
+ return Eisopen;
+ nf->busy = 1;
+ nf->open = 0;
+ nf->qid = f->qid;
+ nf->file = vfIncRef(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;
+ vfIncRef(file);
+ qid = f->qid;
+
+ for(nqid = 0; nqid < nwname; nqid++){
+ if((qid.type & QTDIR) == 0){
+ vtSetError(Enotdir);
+ break;
+ }
+ if(!permf(file, f->user, Pexec)) {
+ vtSetError(Eperm);
+ break;
+ }
+ nfile = _vfWalk(file, rhdr.wname[nqid]);
+ if(nfile == nil)
+ break;
+ vfDecRef(file);
+ file = nfile;
+ qid.type = QTFILE;
+ if(vfIsDir(file))
+ qid.type = QTDIR;
+ qid.vers = vfGetMcount(file);
+ qid.path = vfGetId(file);
+ thdr.wqid[nqid] = qid;
+ }
+
+ thdr.nwqid = nqid;
+
+ if(nqid == nwname){
+ /* success */
+ f->qid = thdr.wqid[nqid-1];
+ vfDecRef(f->file);
+ f->file = file;
+ return 0;
+ }
+
+ vfDecRef(file);
+ if(nf != nil)
+ rclunk(nf);
+
+ /* only error on the first element */
+ if(nqid == 0)
+ return vtGetError();
+
+ return 0;
+}
+
+char *
+ropen(Fid *f)
+{
+ int mode, trunc;
+
+ if(f->open)
+ return Eisopen;
+ if(!f->busy)
+ return Enotexist;
+ mode = rhdr.mode;
+ thdr.iounit = messagesize - IOHDRSZ;
+ if(f->qid.type & QTDIR){
+ if(mode != OREAD)
+ return Eperm;
+ if(!perm(f, Pread))
+ return Eperm;
+ thdr.qid = f->qid;
+ f->db = nil;
+ f->open = 1;
+ return 0;
+ }
+ if(mode & ORCLOSE)
+ return Erdonly;
+ trunc = mode & OTRUNC;
+ mode &= OPERM;
+ if(mode==OWRITE || mode==ORDWR || trunc)
+ if(!perm(f, Pwrite))
+ return Eperm;
+ if(mode==OREAD || mode==ORDWR)
+ if(!perm(f, Pread))
+ return Eperm;
+ if(mode==OEXEC)
+ if(!perm(f, Pexec))
+ return 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 Eisopen;
+ if(!fid->busy)
+ return Enotexist;
+ if(vfsIsReadOnly(fs))
+ return Erdonly;
+ vf = fid->file;
+ if(!vfIsDir(vf))
+ return Enotdir;
+ if(!permf(vf, fid->user, Pwrite))
+ return Eperm;
+
+ mode = rhdr.perm & 0777;
+
+ if(rhdr.perm & DMDIR){
+ if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))
+ return Emode;
+ switch(rhdr.mode & OPERM){
+ default:
+ return Emode;
+ case OEXEC:
+ case OREAD:
+ break;
+ case OWRITE:
+ case ORDWR:
+ return Eperm;
+ }
+ mode |= ModeDir;
+ }
+ vf = vfCreate(vf, rhdr.name, mode, "none");
+ if(vf == nil)
+ return vtGetError();
+ vfDecRef(fid->file);
+
+ fid->file = vf;
+ fid->qid.type = QTFILE;
+ if(vfIsDir(vf))
+ fid->qid.type = QTDIR;
+ fid->qid.vers = vfGetMcount(vf);
+ fid->qid.path = vfGetId(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;
+ int n;
+
+ if(!f->busy)
+ return 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
+ n = vfRead(vf, buf, cnt, off);
+ if(n < 0) {
+ err = vtGetError();
+ if(err == nil)
+ err = "unknown error!";
+ return err;
+ }
+ thdr.count = n;
+ return 0;
+}
+
+char*
+rwrite(Fid *f)
+{
+ char *buf;
+ vlong off;
+ int cnt;
+ VacFile *vf;
+
+ if(!f->busy)
+ return Enotexist;
+ vf = f->file;
+ thdr.count = 0;
+ off = rhdr.offset;
+ buf = rhdr.data;
+ cnt = rhdr.count;
+ if(f->qid.type & QTDIR)
+ return "file is a directory";
+ thdr.count = vfWrite(vf, buf, cnt, off, "none");
+ if(thdr.count < 0) {
+fprint(2, "write failed: %s\n", vtGetError());
+ return vtGetError();
+ }
+ return 0;
+}
+
+char *
+rclunk(Fid *f)
+{
+ f->busy = 0;
+ f->open = 0;
+ vtMemFree(f->user);
+ f->user = nil;
+ vfDecRef(f->file);
+ f->file = nil;
+ dirBufFree(f->db);
+ f->db = nil;
+ return 0;
+}
+
+char *
+rremove(Fid *f)
+{
+ VacFile *vf, *vfp;
+ char *err = nil;
+
+ if(!f->busy)
+ return Enotexist;
+ vf = f->file;
+ vfp = vfGetParent(vf);
+
+ if(!permf(vfp, f->user, Pwrite)) {
+ err = Eperm;
+ goto Exit;
+ }
+
+ if(!vfRemove(vf, "none")) {
+print("vfRemove failed\n");
+ err = vtGetError();
+ }
+
+Exit:
+ vfDecRef(vfp);
+ rclunk(f);
+ return err;
+}
+
+char *
+rstat(Fid *f)
+{
+ VacDir dir;
+ static uchar statbuf[1024];
+
+ if(!f->busy)
+ return Enotexist;
+ vfGetDir(f->file, &dir);
+ thdr.stat = statbuf;
+ thdr.nstat = vdStat(&dir, thdr.stat, sizeof statbuf);
+ vdCleanup(&dir);
+ return 0;
+}
+
+char *
+rwstat(Fid *f)
+{
+ if(!f->busy)
+ return Enotexist;
+ return Erdonly;
+}
+
+int
+vdStat(VacDir *vd, uchar *p, int np)
+{
+ Dir dir;
+
+ memset(&dir, 0, sizeof(dir));
+
+ /*
+ * Where do path and version come from
+ */
+ dir.qid.path = vd->qid;
+ 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;
+ }
+
+ 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;
+
+ return convD2M(&dir, p, np);
+}
+
+DirBuf*
+dirBufAlloc(VacFile *vf)
+{
+ DirBuf *db;
+
+ db = vtMemAllocZ(sizeof(DirBuf));
+ db->vde = vfDirEnum(vf);
+ return db;
+}
+
+VacDir *
+dirBufGet(DirBuf *db)
+{
+ VacDir *vd;
+ int n;
+
+ if(db->eof)
+ return nil;
+
+ if(db->i >= db->n) {
+ n = vdeRead(db->vde, db->buf, DirBufSize);
+ if(n < 0)
+ return nil;
+ db->i = 0;
+ db->n = n;
+ if(n == 0) {
+ db->eof = 1;
+ return nil;
+ }
+ }
+
+ vd = db->buf + db->i;
+ db->i++;
+
+ return vd;
+}
+
+int
+dirBufUnget(DirBuf *db)
+{
+ assert(db->i > 0);
+ db->i--;
+ return 1;
+}
+
+void
+dirBufFree(DirBuf *db)
+{
+ int i;
+
+ if(db == nil)
+ return;
+
+ for(i=db->i; i<db->n; i++)
+ vdCleanup(db->buf + i);
+ vdeFree(db->vde);
+ vtMemFree(db);
+}
+
+int
+vacdirread(Fid *f, char *p, long off, long cnt)
+{
+ int n, nb;
+ VacDir *vd;
+
+ /*
+ * special case of rewinding a directory
+ * otherwise ignore the offset
+ */
+ if(off == 0 && f->db) {
+ dirBufFree(f->db);
+ f->db = nil;
+ }
+
+ if(f->db == nil)
+ f->db = dirBufAlloc(f->file);
+
+ for(nb = 0; nb < cnt; nb += n) {
+ vd = dirBufGet(f->db);
+ if(vd == nil) {
+ if(!f->db->eof)
+ return -1;
+ break;
+ }
+ n = vdStat(vd, (uchar*)p, cnt-nb);
+ if(n <= BIT16SZ) {
+ dirBufUnget(f->db);
+ 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 = vtMemAllocZ(sizeof *f);
+ f->fid = fid;
+ f->next = fids;
+ fids = f;
+ return f;
+}
+
+void
+io(void)
+{
+ char *err;
+ int n;
+
+ for(;;){
+ /*
+ * reading from a pipe or a network device
+ * will give an error after a few eof reads
+ * however, we cannot tell the difference
+ * between a zero-length read and an interrupt
+ * on the processes writing to us,
+ * so we wait for the error
+ */
+ n = read9pmsg(mfd[0], mdata, sizeof mdata);
+ if(n == 0)
+ continue;
+ 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;
+ }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(write(mfd[1], mdata, n) != n)
+ sysfatal("mount write: %r");
+ }
+}
+
+int
+permf(VacFile *vf, char *user, int p)
+{
+ VacDir dir;
+ ulong perm;
+
+ if(!vfGetDir(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
+init(char *file, char *host, long ncache, int readOnly)
+{
+ notify(notifyf);
+ user = getuser();
+
+ fmtinstall('V', vtScoreFmt);
+ fmtinstall('R', vtErrFmt);
+
+ session = vtDial(host, 0);
+ if(session == nil)
+ vtFatal("could not connect to server: %s", vtGetError());
+
+ if(!vtConnect(session, 0))
+ vtFatal("vtConnect: %s", vtGetError());
+
+ fs = vfsOpen(session, file, readOnly, ncache);
+ if(fs == nil)
+ vtFatal("vfsOpen: %s", vtGetError());
+}
+
+void
+shutdown(void)
+{
+ Fid *f;
+
+ for(f = fids; f; f = f->next) {
+ if(!f->busy)
+ continue;
+fprint(2, "open fid: %d\n", f->fid);
+ rclunk(f);
+ }
+
+ vfsClose(fs);
+ vtClose(session);
+}
+