📄 vacfs.c
字号:
#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;voidnotifyf(void *a, char *s){ USED(a); if(strncmp(s, "interrupt", 9) == 0) noted(NCONT); noted(NDFLT);}voidmain(int argc, char *argv[]){ char *defmnt, *defsrv, *srv; int p[2]; char buf[12]; int fd; int stdio = 0; char *host = nil; long ncache = 1000; int readOnly = 1; defmnt = "/n/vac"; defsrv = "vacfs"; ARGBEGIN{ case 'd': fmtinstall('F', fcallfmt); dflag = 1; break; case 'c': ncache = atoi(ARGF()); break; case 'i': defmnt = nil; stdio = 1; mfd[0] = 0; mfd[1] = 1; break; case 'h': host = ARGF(); break; case 'S': defsrv = ARGF(); /*FALLTHROUGH*/ case 's': defmnt = nil; 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){ srv = smprint("/srv/%s", defsrv); fd = create(srv, OWRITE, 0666); if(fd < 0) sysfatal("create of %s failed: %r", srv); sprint(buf, "%d", p[1]); if(write(fd, buf, strlen(buf)) < 0) sysfatal("writing %s: %r", srv); free(srv); } } 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);}voidusage(void){ fprint(2, "usage: %s [-dips]" " [-c ncache]" " [-h host]" " [-m mountpoint]" " [-S srvname]" " 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"; cnt = vfWrite(vf, buf, cnt, off, "none"); if(cnt < 0) {fprint(2, "write failed: %s\n", vtGetError()); return vtGetError(); } thdr.count = cnt; 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;}intvdStat(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;}intdirBufUnget(DirBuf *db){ assert(db->i > 0); db->i--; return 1;}voiddirBufFree(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);}intvacdirread(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;}voidio(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"); }}intpermf(VacFile *vf, char *user, int p){ int ok = 1; 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; ok = 0;Good: vdCleanup(&dir); return ok;}intperm(Fid *f, int p){ return permf(f->file, f->user, p);}voidinit(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());}voidshutdown(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);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -