Blob
1 #include "stdinc.h"2 #include <auth.h>3 #include <fcall.h>4 #include "vac.h"6 typedef struct Fid Fid;7 typedef struct DirBuf DirBuf;9 enum10 {11 OPERM = 0x3, /* mask of all permission types in open mode */12 };14 enum15 {16 DirBufSize = 20,17 };19 struct Fid20 {21 short busy;22 short open;23 int fid;24 char *user;25 Qid qid;26 VacFile *file;28 DirBuf *db;30 Fid *next;31 };33 struct DirBuf34 {35 VacDirEnum *vde;36 VacDir buf[DirBufSize];37 int i, n;38 int eof;39 };41 enum42 {43 Pexec = 1,44 Pwrite = 2,45 Pread = 4,46 Pother = 1,47 Pgroup = 8,48 Powner = 64,49 };51 Fid *fids;52 uchar *data;53 int mfd[2];54 char *user;55 uchar mdata[8192+IOHDRSZ];56 int messagesize = sizeof mdata;57 Fcall rhdr;58 Fcall thdr;59 VacFS *fs;60 VtSession *session;61 int noperm;63 Fid * newfid(int);64 void error(char*);65 void io(void);66 void shutdown(void);67 void usage(void);68 int perm(Fid*, int);69 int permf(VacFile*, char*, int);70 ulong getl(void *p);71 void init(char*, char*, long, int);72 DirBuf *dirBufAlloc(VacFile*);73 VacDir *dirBufGet(DirBuf*);74 int dirBufUnget(DirBuf*);75 void dirBufFree(DirBuf*);76 int vacdirread(Fid *f, char *p, long off, long cnt);77 int vdStat(VacDir *vd, uchar *p, int np);79 char *rflush(Fid*), *rversion(Fid*),80 *rauth(Fid*), *rattach(Fid*), *rwalk(Fid*),81 *ropen(Fid*), *rcreate(Fid*),82 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),83 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);85 char *(*fcalls[])(Fid*) = {86 [Tflush] rflush,87 [Tversion] rversion,88 [Tattach] rattach,89 [Tauth] rauth,90 [Twalk] rwalk,91 [Topen] ropen,92 [Tcreate] rcreate,93 [Tread] rread,94 [Twrite] rwrite,95 [Tclunk] rclunk,96 [Tremove] rremove,97 [Tstat] rstat,98 [Twstat] rwstat,99 };101 char Eperm[] = "permission denied";102 char Enotdir[] = "not a directory";103 char Enotexist[] = "file does not exist";104 char Einuse[] = "file in use";105 char Eexist[] = "file exists";106 char Enotowner[] = "not owner";107 char Eisopen[] = "file already open for I/O";108 char Excl[] = "exclusive use file already open";109 char Ename[] = "illegal name";110 char Erdonly[] = "read only file system";111 char Eio[] = "i/o error";112 char Eempty[] = "directory is not empty";113 char Emode[] = "illegal mode";115 int dflag;117 void118 notifyf(void *a, char *s)119 {120 USED(a);121 if(strncmp(s, "interrupt", 9) == 0)122 noted(NCONT);123 noted(NDFLT);124 }126 void127 main(int argc, char *argv[])128 {129 char *defmnt;130 int p[2];131 char buf[12];132 int fd;133 int stdio = 0;134 char *host = nil;135 long ncache = 1000;136 int readOnly = 1;138 defmnt = "/n/vac";139 ARGBEGIN{140 case 'd':141 fmtinstall('F', fcallfmt);142 dflag = 1;143 break;144 case 'c':145 ncache = atoi(ARGF());146 break;147 case 'i':148 defmnt = 0;149 stdio = 1;150 mfd[0] = 0;151 mfd[1] = 1;152 break;153 case 'h':154 host = ARGF();155 break;156 case 's':157 defmnt = 0;158 break;159 case 'p':160 noperm = 1;161 break;162 case 'm':163 defmnt = ARGF();164 break;165 default:166 usage();167 }ARGEND169 if(argc != 1)170 usage();172 vtAttach();174 init(argv[0], host, ncache, readOnly);176 if(pipe(p) < 0)177 sysfatal("pipe failed: %r");178 if(!stdio){179 mfd[0] = p[0];180 mfd[1] = p[0];181 if(defmnt == 0){182 fd = create("#s/vacfs", OWRITE, 0666);183 if(fd < 0)184 sysfatal("create of /srv/vacfs failed: %r");185 sprint(buf, "%d", p[1]);186 if(write(fd, buf, strlen(buf)) < 0)187 sysfatal("writing /srv/vacfs: %r");188 }189 }191 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){192 case -1:193 sysfatal("fork: %r");194 case 0:195 vtAttach();196 close(p[1]);197 io();198 shutdown();199 break;200 default:201 close(p[0]); /* don't deadlock if child fails */202 if(defmnt && mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0)203 sysfatal("mount failed: %r");204 }205 vtDetach();206 exits(0);207 }209 void210 usage(void)211 {212 fprint(2, "usage: %s [-sd] [-h host] [-c ncache] [-m mountpoint] vacfile\n", argv0);213 exits("usage");214 }216 char*217 rversion(Fid *unused)218 {219 Fid *f;221 USED(unused);223 for(f = fids; f; f = f->next)224 if(f->busy)225 rclunk(f);227 if(rhdr.msize < 256)228 return "version: message size too small";229 messagesize = rhdr.msize;230 if(messagesize > sizeof mdata)231 messagesize = sizeof mdata;232 thdr.msize = messagesize;233 if(strncmp(rhdr.version, "9P2000", 6) != 0)234 return "unrecognized 9P version";235 thdr.version = "9P2000";236 return nil;237 }239 char*240 rflush(Fid *f)241 {242 USED(f);243 return 0;244 }246 char*247 rauth(Fid *f)248 {249 USED(f);250 return "vacfs: authentication not required";251 }253 char*254 rattach(Fid *f)255 {256 /* no authentication for the momment */257 VacFile *file;259 file = vfsGetRoot(fs);260 if(file == nil)261 return vtGetError();262 f->busy = 1;263 f->file = file;264 f->qid = (Qid){vfGetId(f->file), 0, QTDIR};265 thdr.qid = f->qid;266 if(rhdr.uname[0])267 f->user = vtStrDup(rhdr.uname);268 else269 f->user = "none";270 return 0;271 }273 VacFile*274 _vfWalk(VacFile *file, char *name)275 {276 VacFile *n;278 n = vfWalk(file, name);279 if(n)280 return n;281 if(strcmp(name, "SLASH") == 0)282 return vfWalk(file, "/");283 return nil;284 }286 char*287 rwalk(Fid *f)288 {289 VacFile *file, *nfile;290 Fid *nf;291 int nqid, nwname;292 Qid qid;294 if(f->busy == 0)295 return Enotexist;296 nf = nil;297 if(rhdr.fid != rhdr.newfid){298 if(f->open)299 return Eisopen;300 if(f->busy == 0)301 return Enotexist;302 nf = newfid(rhdr.newfid);303 if(nf->busy)304 return Eisopen;305 nf->busy = 1;306 nf->open = 0;307 nf->qid = f->qid;308 nf->file = vfIncRef(f->file);309 nf->user = vtStrDup(f->user);310 f = nf;311 }313 nwname = rhdr.nwname;315 /* easy case */316 if(nwname == 0) {317 thdr.nwqid = 0;318 return 0;319 }321 file = f->file;322 vfIncRef(file);323 qid = f->qid;325 for(nqid = 0; nqid < nwname; nqid++){326 if((qid.type & QTDIR) == 0){327 vtSetError(Enotdir);328 break;329 }330 if(!permf(file, f->user, Pexec)) {331 vtSetError(Eperm);332 break;333 }334 nfile = _vfWalk(file, rhdr.wname[nqid]);335 if(nfile == nil)336 break;337 vfDecRef(file);338 file = nfile;339 qid.type = QTFILE;340 if(vfIsDir(file))341 qid.type = QTDIR;342 qid.vers = vfGetMcount(file);343 qid.path = vfGetId(file);344 thdr.wqid[nqid] = qid;345 }347 thdr.nwqid = nqid;349 if(nqid == nwname){350 /* success */351 f->qid = thdr.wqid[nqid-1];352 vfDecRef(f->file);353 f->file = file;354 return 0;355 }357 vfDecRef(file);358 if(nf != nil)359 rclunk(nf);361 /* only error on the first element */362 if(nqid == 0)363 return vtGetError();365 return 0;366 }368 char *369 ropen(Fid *f)370 {371 int mode, trunc;373 if(f->open)374 return Eisopen;375 if(!f->busy)376 return Enotexist;377 mode = rhdr.mode;378 thdr.iounit = messagesize - IOHDRSZ;379 if(f->qid.type & QTDIR){380 if(mode != OREAD)381 return Eperm;382 if(!perm(f, Pread))383 return Eperm;384 thdr.qid = f->qid;385 f->db = nil;386 f->open = 1;387 return 0;388 }389 if(mode & ORCLOSE)390 return Erdonly;391 trunc = mode & OTRUNC;392 mode &= OPERM;393 if(mode==OWRITE || mode==ORDWR || trunc)394 if(!perm(f, Pwrite))395 return Eperm;396 if(mode==OREAD || mode==ORDWR)397 if(!perm(f, Pread))398 return Eperm;399 if(mode==OEXEC)400 if(!perm(f, Pexec))401 return Eperm;402 thdr.qid = f->qid;403 thdr.iounit = messagesize - IOHDRSZ;404 f->open = 1;405 return 0;406 }408 char*409 rcreate(Fid* fid)410 {411 VacFile *vf;412 ulong mode;414 if(fid->open)415 return Eisopen;416 if(!fid->busy)417 return Enotexist;418 if(vfsIsReadOnly(fs))419 return Erdonly;420 vf = fid->file;421 if(!vfIsDir(vf))422 return Enotdir;423 if(!permf(vf, fid->user, Pwrite))424 return Eperm;426 mode = rhdr.perm & 0777;428 if(rhdr.perm & DMDIR){429 if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))430 return Emode;431 switch(rhdr.mode & OPERM){432 default:433 return Emode;434 case OEXEC:435 case OREAD:436 break;437 case OWRITE:438 case ORDWR:439 return Eperm;440 }441 mode |= ModeDir;442 }443 vf = vfCreate(vf, rhdr.name, mode, "none");444 if(vf == nil)445 return vtGetError();446 vfDecRef(fid->file);448 fid->file = vf;449 fid->qid.type = QTFILE;450 if(vfIsDir(vf))451 fid->qid.type = QTDIR;452 fid->qid.vers = vfGetMcount(vf);453 fid->qid.path = vfGetId(vf);455 thdr.qid = fid->qid;456 thdr.iounit = messagesize - IOHDRSZ;458 return 0;459 }461 char*462 rread(Fid *f)463 {464 char *buf;465 vlong off;466 int cnt;467 VacFile *vf;468 char *err;469 int n;471 if(!f->busy)472 return Enotexist;473 vf = f->file;474 thdr.count = 0;475 off = rhdr.offset;476 buf = thdr.data;477 cnt = rhdr.count;478 if(f->qid.type & QTDIR)479 n = vacdirread(f, buf, off, cnt);480 else481 n = vfRead(vf, buf, cnt, off);482 if(n < 0) {483 err = vtGetError();484 if(err == nil)485 err = "unknown error!";486 return err;487 }488 thdr.count = n;489 return 0;490 }492 char*493 rwrite(Fid *f)494 {495 char *buf;496 vlong off;497 int cnt;498 VacFile *vf;500 if(!f->busy)501 return Enotexist;502 vf = f->file;503 thdr.count = 0;504 off = rhdr.offset;505 buf = rhdr.data;506 cnt = rhdr.count;507 if(f->qid.type & QTDIR)508 return "file is a directory";509 thdr.count = vfWrite(vf, buf, cnt, off, "none");510 if(thdr.count < 0) {511 fprint(2, "write failed: %s\n", vtGetError());512 return vtGetError();513 }514 return 0;515 }517 char *518 rclunk(Fid *f)519 {520 f->busy = 0;521 f->open = 0;522 vtMemFree(f->user);523 f->user = nil;524 vfDecRef(f->file);525 f->file = nil;526 dirBufFree(f->db);527 f->db = nil;528 return 0;529 }531 char *532 rremove(Fid *f)533 {534 VacFile *vf, *vfp;535 char *err = nil;537 if(!f->busy)538 return Enotexist;539 vf = f->file;540 vfp = vfGetParent(vf);542 if(!permf(vfp, f->user, Pwrite)) {543 err = Eperm;544 goto Exit;545 }547 if(!vfRemove(vf, "none")) {548 print("vfRemove failed\n");549 err = vtGetError();550 }552 Exit:553 vfDecRef(vfp);554 rclunk(f);555 return err;556 }558 char *559 rstat(Fid *f)560 {561 VacDir dir;562 static uchar statbuf[1024];564 if(!f->busy)565 return Enotexist;566 vfGetDir(f->file, &dir);567 thdr.stat = statbuf;568 thdr.nstat = vdStat(&dir, thdr.stat, sizeof statbuf);569 vdCleanup(&dir);570 return 0;571 }573 char *574 rwstat(Fid *f)575 {576 if(!f->busy)577 return Enotexist;578 return Erdonly;579 }581 int582 vdStat(VacDir *vd, uchar *p, int np)583 {584 Dir dir;586 memset(&dir, 0, sizeof(dir));588 /*589 * Where do path and version come from590 */591 dir.qid.path = vd->qid;592 dir.qid.vers = vd->mcount;593 dir.mode = vd->mode & 0777;594 if(vd->mode & ModeAppend){595 dir.qid.type |= QTAPPEND;596 dir.mode |= DMAPPEND;597 }598 if(vd->mode & ModeExclusive){599 dir.qid.type |= QTEXCL;600 dir.mode |= DMEXCL;601 }602 if(vd->mode & ModeDir){603 dir.qid.type |= QTDIR;604 dir.mode |= DMDIR;605 }607 dir.atime = vd->atime;608 dir.mtime = vd->mtime;609 dir.length = vd->size;611 dir.name = vd->elem;612 dir.uid = vd->uid;613 dir.gid = vd->gid;614 dir.muid = vd->mid;616 return convD2M(&dir, p, np);617 }619 DirBuf*620 dirBufAlloc(VacFile *vf)621 {622 DirBuf *db;624 db = vtMemAllocZ(sizeof(DirBuf));625 db->vde = vfDirEnum(vf);626 return db;627 }629 VacDir *630 dirBufGet(DirBuf *db)631 {632 VacDir *vd;633 int n;635 if(db->eof)636 return nil;638 if(db->i >= db->n) {639 n = vdeRead(db->vde, db->buf, DirBufSize);640 if(n < 0)641 return nil;642 db->i = 0;643 db->n = n;644 if(n == 0) {645 db->eof = 1;646 return nil;647 }648 }650 vd = db->buf + db->i;651 db->i++;653 return vd;654 }656 int657 dirBufUnget(DirBuf *db)658 {659 assert(db->i > 0);660 db->i--;661 return 1;662 }664 void665 dirBufFree(DirBuf *db)666 {667 int i;669 if(db == nil)670 return;672 for(i=db->i; i<db->n; i++)673 vdCleanup(db->buf + i);674 vdeFree(db->vde);675 vtMemFree(db);676 }678 int679 vacdirread(Fid *f, char *p, long off, long cnt)680 {681 int n, nb;682 VacDir *vd;684 /*685 * special case of rewinding a directory686 * otherwise ignore the offset687 */688 if(off == 0 && f->db) {689 dirBufFree(f->db);690 f->db = nil;691 }693 if(f->db == nil)694 f->db = dirBufAlloc(f->file);696 for(nb = 0; nb < cnt; nb += n) {697 vd = dirBufGet(f->db);698 if(vd == nil) {699 if(!f->db->eof)700 return -1;701 break;702 }703 n = vdStat(vd, (uchar*)p, cnt-nb);704 if(n <= BIT16SZ) {705 dirBufUnget(f->db);706 break;707 }708 vdCleanup(vd);709 p += n;710 }711 return nb;712 }714 Fid *715 newfid(int fid)716 {717 Fid *f, *ff;719 ff = 0;720 for(f = fids; f; f = f->next)721 if(f->fid == fid)722 return f;723 else if(!ff && !f->busy)724 ff = f;725 if(ff){726 ff->fid = fid;727 return ff;728 }729 f = vtMemAllocZ(sizeof *f);730 f->fid = fid;731 f->next = fids;732 fids = f;733 return f;734 }736 void737 io(void)738 {739 char *err;740 int n;742 for(;;){743 /*744 * reading from a pipe or a network device745 * will give an error after a few eof reads746 * however, we cannot tell the difference747 * between a zero-length read and an interrupt748 * on the processes writing to us,749 * so we wait for the error750 */751 n = read9pmsg(mfd[0], mdata, sizeof mdata);752 if(n == 0)753 continue;754 if(n < 0)755 break;756 if(convM2S(mdata, n, &rhdr) != n)757 sysfatal("convM2S conversion error");759 if(dflag)760 fprint(2, "vacfs:<-%F\n", &rhdr);762 thdr.data = (char*)mdata + IOHDRSZ;763 if(!fcalls[rhdr.type])764 err = "bad fcall type";765 else766 err = (*fcalls[rhdr.type])(newfid(rhdr.fid));767 if(err){768 thdr.type = Rerror;769 thdr.ename = err;770 }else{771 thdr.type = rhdr.type + 1;772 thdr.fid = rhdr.fid;773 }774 thdr.tag = rhdr.tag;775 if(dflag)776 fprint(2, "vacfs:->%F\n", &thdr);777 n = convS2M(&thdr, mdata, messagesize);778 if(write(mfd[1], mdata, n) != n)779 sysfatal("mount write: %r");780 }781 }783 int784 permf(VacFile *vf, char *user, int p)785 {786 VacDir dir;787 ulong perm;789 if(!vfGetDir(vf, &dir))790 return 0;791 perm = dir.mode & 0777;792 if(noperm)793 goto Good;794 if((p*Pother) & perm)795 goto Good;796 if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm))797 goto Good;798 if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm))799 goto Good;800 vdCleanup(&dir);801 return 0;802 Good:803 vdCleanup(&dir);804 return 1;805 }807 int808 perm(Fid *f, int p)809 {810 return permf(f->file, f->user, p);811 }813 void814 init(char *file, char *host, long ncache, int readOnly)815 {816 notify(notifyf);817 user = getuser();819 fmtinstall('V', vtScoreFmt);820 fmtinstall('R', vtErrFmt);822 session = vtDial(host, 0);823 if(session == nil)824 vtFatal("could not connect to server: %s", vtGetError());826 if(!vtConnect(session, 0))827 vtFatal("vtConnect: %s", vtGetError());829 fs = vfsOpen(session, file, readOnly, ncache);830 if(fs == nil)831 vtFatal("vfsOpen: %s", vtGetError());832 }834 void835 shutdown(void)836 {837 Fid *f;839 for(f = fids; f; f = f->next) {840 if(!f->busy)841 continue;842 fprint(2, "open fid: %d\n", f->fid);843 rclunk(f);844 }846 vfsClose(fs);847 vtClose(session);848 }