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 enum
10 {
11 OPERM = 0x3, /* mask of all permission types in open mode */
12 };
14 enum
15 {
16 DirBufSize = 20,
17 };
19 struct Fid
20 {
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 DirBuf
34 {
35 VacDirEnum *vde;
36 VacDir buf[DirBufSize];
37 int i, n;
38 int eof;
39 };
41 enum
42 {
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 void
118 notifyf(void *a, char *s)
120 USED(a);
121 if(strncmp(s, "interrupt", 9) == 0)
122 noted(NCONT);
123 noted(NDFLT);
126 void
127 main(int argc, char *argv[])
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 }ARGEND
169 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");
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");
205 vtDetach();
206 exits(0);
209 void
210 usage(void)
212 fprint(2, "usage: %s [-sd] [-h host] [-c ncache] [-m mountpoint] vacfile\n", argv0);
213 exits("usage");
216 char*
217 rversion(Fid *unused)
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;
239 char*
240 rflush(Fid *f)
242 USED(f);
243 return 0;
246 char*
247 rauth(Fid *f)
249 USED(f);
250 return "vacfs: authentication not required";
253 char*
254 rattach(Fid *f)
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 else
269 f->user = "none";
270 return 0;
273 VacFile*
274 _vfWalk(VacFile *file, char *name)
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;
286 char*
287 rwalk(Fid *f)
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;
313 nwname = rhdr.nwname;
315 /* easy case */
316 if(nwname == 0) {
317 thdr.nwqid = 0;
318 return 0;
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;
330 if(!permf(file, f->user, Pexec)) {
331 vtSetError(Eperm);
332 break;
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;
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;
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;
368 char *
369 ropen(Fid *f)
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;
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;
408 char*
409 rcreate(Fid* fid)
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;
441 mode |= ModeDir;
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;
461 char*
462 rread(Fid *f)
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 else
481 n = vfRead(vf, buf, cnt, off);
482 if(n < 0) {
483 err = vtGetError();
484 if(err == nil)
485 err = "unknown error!";
486 return err;
488 thdr.count = n;
489 return 0;
492 char*
493 rwrite(Fid *f)
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();
514 return 0;
517 char *
518 rclunk(Fid *f)
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;
531 char *
532 rremove(Fid *f)
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;
547 if(!vfRemove(vf, "none")) {
548 print("vfRemove failed\n");
549 err = vtGetError();
552 Exit:
553 vfDecRef(vfp);
554 rclunk(f);
555 return err;
558 char *
559 rstat(Fid *f)
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;
573 char *
574 rwstat(Fid *f)
576 if(!f->busy)
577 return Enotexist;
578 return Erdonly;
581 int
582 vdStat(VacDir *vd, uchar *p, int np)
584 Dir dir;
586 memset(&dir, 0, sizeof(dir));
588 /*
589 * Where do path and version come from
590 */
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;
598 if(vd->mode & ModeExclusive){
599 dir.qid.type |= QTEXCL;
600 dir.mode |= DMEXCL;
602 if(vd->mode & ModeDir){
603 dir.qid.type |= QTDIR;
604 dir.mode |= DMDIR;
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);
619 DirBuf*
620 dirBufAlloc(VacFile *vf)
622 DirBuf *db;
624 db = vtMemAllocZ(sizeof(DirBuf));
625 db->vde = vfDirEnum(vf);
626 return db;
629 VacDir *
630 dirBufGet(DirBuf *db)
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;
650 vd = db->buf + db->i;
651 db->i++;
653 return vd;
656 int
657 dirBufUnget(DirBuf *db)
659 assert(db->i > 0);
660 db->i--;
661 return 1;
664 void
665 dirBufFree(DirBuf *db)
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);
678 int
679 vacdirread(Fid *f, char *p, long off, long cnt)
681 int n, nb;
682 VacDir *vd;
684 /*
685 * special case of rewinding a directory
686 * otherwise ignore the offset
687 */
688 if(off == 0 && f->db) {
689 dirBufFree(f->db);
690 f->db = nil;
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;
703 n = vdStat(vd, (uchar*)p, cnt-nb);
704 if(n <= BIT16SZ) {
705 dirBufUnget(f->db);
706 break;
708 vdCleanup(vd);
709 p += n;
711 return nb;
714 Fid *
715 newfid(int fid)
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;
729 f = vtMemAllocZ(sizeof *f);
730 f->fid = fid;
731 f->next = fids;
732 fids = f;
733 return f;
736 void
737 io(void)
739 char *err;
740 int n;
742 for(;;){
743 /*
744 * reading from a pipe or a network device
745 * will give an error after a few eof reads
746 * however, we cannot tell the difference
747 * between a zero-length read and an interrupt
748 * on the processes writing to us,
749 * so we wait for the error
750 */
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 else
766 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;
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");
783 int
784 permf(VacFile *vf, char *user, int p)
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;
807 int
808 perm(Fid *f, int p)
810 return permf(f->file, f->user, p);
813 void
814 init(char *file, char *host, long ncache, int readOnly)
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());
834 void
835 shutdown(void)
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);
846 vfsClose(fs);
847 vtClose(session);