Blob


1 #include "stdinc.h"
2 #include <auth.h>
3 #include <fcall.h>
4 #include <thread.h>
5 #include "vac.h"
7 typedef struct Fid Fid;
8 typedef struct DirBuf DirBuf;
10 enum
11 {
12 OPERM = 0x3, /* mask of all permission types in open mode */
13 };
15 enum
16 {
17 DirBufSize = 20,
18 };
20 struct Fid
21 {
22 short busy;
23 short open;
24 int fid;
25 char *user;
26 Qid qid;
27 VacFile *file;
29 DirBuf *db;
31 Fid *next;
32 };
34 struct DirBuf
35 {
36 VacDirEnum *vde;
37 VacDir buf[DirBufSize];
38 int i, n;
39 int eof;
40 };
42 enum
43 {
44 Pexec = 1,
45 Pwrite = 2,
46 Pread = 4,
47 Pother = 1,
48 Pgroup = 8,
49 Powner = 64,
50 };
52 Fid *fids;
53 uchar *data;
54 int mfd[2];
55 char *user;
56 uchar mdata[8192+IOHDRSZ];
57 int messagesize = sizeof mdata;
58 Fcall rhdr;
59 Fcall thdr;
60 VacFs *fs;
61 VtConn *conn;
62 // VtSession *session;
63 int noperm;
64 int dotu;
66 Fid * newfid(int);
67 void error(char*);
68 void io(void);
69 void vacshutdown(void);
70 void usage(void);
71 int perm(Fid*, int);
72 int permf(VacFile*, char*, int);
73 ulong getl(void *p);
74 void init(char*, char*, long, int);
75 DirBuf *dirBufAlloc(VacFile*);
76 VacDir *dirBufGet(DirBuf*);
77 int dirBufUnget(DirBuf*);
78 void dirBufFree(DirBuf*);
79 int vacdirread(Fid *f, char *p, long off, long cnt);
80 int vdStat(VacFile *parent, VacDir *vd, uchar *p, int np);
81 void srv(void* a);
84 char *rflush(Fid*), *rversion(Fid*),
85 *rauth(Fid*), *rattach(Fid*), *rwalk(Fid*),
86 *ropen(Fid*), *rcreate(Fid*),
87 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
88 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
90 char *(*fcalls[Tmax])(Fid*);
92 void
93 initfcalls(void)
94 {
95 fcalls[Tflush]= rflush;
96 fcalls[Tversion]= rversion;
97 fcalls[Tattach]= rattach;
98 fcalls[Tauth]= rauth;
99 fcalls[Twalk]= rwalk;
100 fcalls[Topen]= ropen;
101 fcalls[Tcreate]= rcreate;
102 fcalls[Tread]= rread;
103 fcalls[Twrite]= rwrite;
104 fcalls[Tclunk]= rclunk;
105 fcalls[Tremove]= rremove;
106 fcalls[Tstat]= rstat;
107 fcalls[Twstat]= rwstat;
110 char Eperm[] = "permission denied";
111 char Enotdir[] = "not a directory";
112 char Enotexist[] = "file does not exist";
113 char Einuse[] = "file in use";
114 char Eexist[] = "file exists";
115 char Enotowner[] = "not owner";
116 char Eisopen[] = "file already open for I/O";
117 char Excl[] = "exclusive use file already open";
118 char Ename[] = "illegal name";
119 char Erdonly[] = "read only file system";
120 char Eio[] = "i/o error";
121 char Eempty[] = "directory is not empty";
122 char Emode[] = "illegal mode";
124 int dflag;
126 void
127 notifyf(void *a, char *s)
129 USED(a);
130 if(strncmp(s, "interrupt", 9) == 0)
131 noted(NCONT);
132 noted(NDFLT);
135 void
136 threadmain(int argc, char *argv[])
138 char *defsrv, *q;
139 int p[2], l;
140 int stdio = 0;
141 char *host = nil;
142 long ncache = 1000;
143 int readOnly = 1;
145 fmtinstall('H', encodefmt);
146 fmtinstall('V', vtscorefmt);
147 fmtinstall('F', vtfcallfmt);
149 defsrv = nil;
150 ARGBEGIN{
151 case 'd':
152 fmtinstall('F', fcallfmt);
153 dflag = 1;
154 break;
155 case 'c':
156 ncache = atoi(EARGF(usage()));
157 break;
158 case 'i':
159 stdio = 1;
160 mfd[0] = 0;
161 mfd[1] = 1;
162 break;
163 case 'h':
164 host = EARGF(usage());
165 break;
166 case 's':
167 defsrv = EARGF(usage());
168 break;
169 case 'p':
170 noperm = 1;
171 break;
172 case 'V':
173 chattyventi = 1;
174 break;
175 default:
176 usage();
177 }ARGEND
179 if(argc != 1)
180 usage();
182 initfcalls();
183 init(argv[0], host, ncache, readOnly);
185 if(pipe(p) < 0)
186 sysfatal("pipe failed: %r");
188 mfd[0] = p[0];
189 mfd[1] = p[0];
190 proccreate(srv, 0, 32 * 1024);
192 if(defsrv == nil){
193 q = strrchr(argv[0], '/');
194 if(q)
195 q++;
196 else
197 q = argv[0];
198 defsrv = vtmalloc(6+strlen(q)+1);
199 strcpy(defsrv, "vacfs.");
200 strcat(defsrv, q);
201 l = strlen(defsrv);
202 if(strcmp(defsrv+l-4, ".vac") == 0)
203 defsrv[l-4] = 0;
206 if(post9pservice(p[1], defsrv) != 0)
207 sysfatal("post9pservice");
209 threadexits(0);
212 void
213 srv(void *a)
215 io();
216 vacshutdown();
219 void
220 usage(void)
222 fprint(2, "usage: %s [-sd] [-h host] [-c ncache] [-m mountpoint] vacfile\n", argv0);
223 threadexitsall("usage");
226 char*
227 rversion(Fid *unused)
229 Fid *f;
231 USED(unused);
233 for(f = fids; f; f = f->next)
234 if(f->busy)
235 rclunk(f);
237 if(rhdr.msize < 256)
238 return vtstrdup("version: message size too small");
239 messagesize = rhdr.msize;
240 if(messagesize > sizeof mdata)
241 messagesize = sizeof mdata;
242 thdr.msize = messagesize;
243 if(strncmp(rhdr.version, "9P2000", 6) != 0)
244 return vtstrdup("unrecognized 9P version");
245 thdr.version = "9P2000";
246 if(strncmp(rhdr.version, "9P2000.u", 8) == 0){
247 dotu = 1;
248 thdr.version = "9P2000.u";
250 return nil;
253 char*
254 rflush(Fid *f)
256 USED(f);
257 return 0;
260 char*
261 rauth(Fid *f)
263 USED(f);
264 return vtstrdup("vacfs: authentication not required");
267 char*
268 rattach(Fid *f)
270 /* no authentication for the momment */
271 VacFile *file;
272 char err[80];
274 file = vacfsgetroot(fs);
275 if(file == nil) {
276 rerrstr(err, sizeof err);
277 return vtstrdup(err);
280 f->busy = 1;
281 f->file = file;
282 f->qid.path = vacfilegetid(f->file);
283 f->qid.vers = 0;
284 f->qid.type = QTDIR;
285 thdr.qid = f->qid;
286 if(rhdr.uname[0])
287 f->user = vtstrdup(rhdr.uname);
288 else
289 f->user = "none";
290 return 0;
293 VacFile*
294 _vfWalk(VacFile *file, char *name)
296 VacFile *n;
298 n = vacfilewalk(file, name);
299 if(n)
300 return n;
301 if(strcmp(name, "SLASH") == 0)
302 return vacfilewalk(file, "/");
303 return nil;
306 char*
307 rwalk(Fid *f)
309 VacFile *file, *nfile;
310 Fid *nf;
311 int nqid, nwname;
312 Qid qid;
313 char *err = nil;
315 if(f->busy == 0)
316 return Enotexist;
317 nf = nil;
318 if(rhdr.fid != rhdr.newfid){
319 if(f->open)
320 return vtstrdup(Eisopen);
321 if(f->busy == 0)
322 return vtstrdup(Enotexist);
323 nf = newfid(rhdr.newfid);
324 if(nf->busy)
325 return vtstrdup(Eisopen);
326 nf->busy = 1;
327 nf->open = 0;
328 nf->qid = f->qid;
329 nf->file = vacfileincref(f->file);
330 nf->user = vtstrdup(f->user);
331 f = nf;
334 nwname = rhdr.nwname;
336 /* easy case */
337 if(nwname == 0) {
338 thdr.nwqid = 0;
339 return 0;
342 file = f->file;
343 vacfileincref(file);
344 qid = f->qid;
346 for(nqid = 0; nqid < nwname; nqid++){
347 if((qid.type & QTDIR) == 0){
348 err = Enotdir;
349 break;
351 if(!permf(file, f->user, Pexec)) {
352 err = Eperm;
353 break;
355 nfile = _vfWalk(file, rhdr.wname[nqid]);
356 if(nfile == nil)
357 break;
358 vacfiledecref(file);
359 file = nfile;
360 qid.type = QTFILE;
361 if(vacfileisdir(file))
362 qid.type = QTDIR;
363 if(vacfilegetmode(file)&ModeLink)
364 qid.type = QTSYMLINK;
365 qid.vers = vacfilegetmcount(file);
366 qid.path = vacfilegetid(file);
367 thdr.wqid[nqid] = qid;
370 thdr.nwqid = nqid;
372 if(nqid == nwname){
373 /* success */
374 f->qid = thdr.wqid[nqid-1];
375 vacfiledecref(f->file);
376 f->file = file;
377 return 0;
380 vacfiledecref(file);
381 if(nf != nil)
382 rclunk(nf);
384 /* only error on the first element */
385 if(nqid == 0)
386 return vtstrdup(err);
388 return 0;
391 char *
392 ropen(Fid *f)
394 int mode, trunc;
396 if(f->open)
397 return vtstrdup(Eisopen);
398 if(!f->busy)
399 return vtstrdup(Enotexist);
401 mode = rhdr.mode;
402 thdr.iounit = messagesize - IOHDRSZ;
403 if(f->qid.type & QTDIR){
404 if(mode != OREAD)
405 return vtstrdup(Eperm);
406 if(!perm(f, Pread))
407 return vtstrdup(Eperm);
408 thdr.qid = f->qid;
409 f->db = nil;
410 f->open = 1;
411 return 0;
413 if(mode & ORCLOSE)
414 return vtstrdup(Erdonly);
415 trunc = mode & OTRUNC;
416 mode &= OPERM;
417 if(mode==OWRITE || mode==ORDWR || trunc)
418 if(!perm(f, Pwrite))
419 return vtstrdup(Eperm);
420 if(mode==OREAD || mode==ORDWR)
421 if(!perm(f, Pread))
422 return vtstrdup(Eperm);
423 if(mode==OEXEC)
424 if(!perm(f, Pexec))
425 return vtstrdup(Eperm);
426 thdr.qid = f->qid;
427 thdr.iounit = messagesize - IOHDRSZ;
428 f->open = 1;
429 return 0;
432 char*
433 rcreate(Fid* fid)
435 VacFile *vf;
436 ulong mode;
438 if(fid->open)
439 return vtstrdup(Eisopen);
440 if(!fid->busy)
441 return vtstrdup(Enotexist);
442 if(fs->mode & ModeSnapshot)
443 return vtstrdup(Erdonly);
444 vf = fid->file;
445 if(!vacfileisdir(vf))
446 return vtstrdup(Enotdir);
447 if(!permf(vf, fid->user, Pwrite))
448 return vtstrdup(Eperm);
450 mode = rhdr.perm & 0777;
452 if(rhdr.perm & DMDIR){
453 if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))
454 return vtstrdup(Emode);
455 switch(rhdr.mode & OPERM){
456 default:
457 return vtstrdup(Emode);
458 case OEXEC:
459 case OREAD:
460 break;
461 case OWRITE:
462 case ORDWR:
463 return vtstrdup(Eperm);
465 mode |= ModeDir;
467 vf = vacfilecreate(vf, rhdr.name, mode, "none");
468 if(vf == nil) {
469 char err[80];
470 rerrstr(err, sizeof err);
472 return vtstrdup(err);
475 vacfiledecref(fid->file);
477 fid->file = vf;
478 fid->qid.type = QTFILE;
479 if(vacfileisdir(vf))
480 fid->qid.type = QTDIR;
481 fid->qid.vers = vacfilegetmcount(vf);
482 fid->qid.path = vacfilegetid(vf);
484 thdr.qid = fid->qid;
485 thdr.iounit = messagesize - IOHDRSZ;
487 return 0;
490 char*
491 rread(Fid *f)
493 char *buf;
494 vlong off;
495 int cnt;
496 VacFile *vf;
497 char err[80];
498 int n;
500 if(!f->busy)
501 return vtstrdup(Enotexist);
502 vf = f->file;
503 thdr.count = 0;
504 off = rhdr.offset;
505 buf = thdr.data;
506 cnt = rhdr.count;
507 if(f->qid.type & QTDIR)
508 n = vacdirread(f, buf, off, cnt);
509 else if(vacfilegetmode(f->file)&ModeDevice)
510 return vtstrdup("device");
511 else if(vacfilegetmode(f->file)&ModeLink)
512 return vtstrdup("symbolic link");
513 else if(vacfilegetmode(f->file)&ModeNamedPipe)
514 return vtstrdup("named pipe");
515 else
516 n = vacfileread(vf, buf, cnt, off);
517 if(n < 0) {
518 rerrstr(err, sizeof err);
519 return vtstrdup(err);
521 thdr.count = n;
522 return 0;
525 char*
526 rwrite(Fid *f)
528 char *buf;
529 vlong off;
530 int cnt;
531 VacFile *vf;
533 if(!f->busy)
534 return vtstrdup(Enotexist);
535 vf = f->file;
536 thdr.count = 0;
537 off = rhdr.offset;
538 buf = rhdr.data;
539 cnt = rhdr.count;
540 if(f->qid.type & QTDIR)
541 return "file is a directory";
542 thdr.count = vacfilewrite(vf, buf, cnt, off, "none");
543 if(thdr.count < 0) {
544 char err[80];
546 rerrstr(err, sizeof err);
547 fprint(2, "write failed: %s\n", err);
548 return vtstrdup(err);
550 return 0;
553 char *
554 rclunk(Fid *f)
556 f->busy = 0;
557 f->open = 0;
558 vtfree(f->user);
559 f->user = nil;
560 if(f->file)
561 vacfiledecref(f->file);
562 f->file = nil;
563 dirBufFree(f->db);
564 f->db = nil;
565 return 0;
568 char *
569 rremove(Fid *f)
571 VacFile *vf, *vfp;
572 char errbuf[80];
573 char *err = nil;
575 if(!f->busy)
576 return vtstrdup(Enotexist);
577 vf = f->file;
578 vfp = vacfilegetparent(vf);
580 if(!permf(vfp, f->user, Pwrite)) {
581 err = Eperm;
582 goto Exit;
585 if(!vacfileremove(vf, "none")) {
586 rerrstr(errbuf, sizeof errbuf);
587 print("vfRemove failed: %s\n", errbuf);
589 err = errbuf;
592 Exit:
593 vacfiledecref(vfp);
594 rclunk(f);
595 return vtstrdup(err);
598 char *
599 rstat(Fid *f)
601 VacDir dir;
602 static uchar statbuf[1024];
603 VacFile *parent;
605 if(!f->busy)
606 return vtstrdup(Enotexist);
607 parent = vacfilegetparent(f->file);
608 vacfilegetdir(f->file, &dir);
609 thdr.stat = statbuf;
610 thdr.nstat = vdStat(parent, &dir, thdr.stat, sizeof statbuf);
611 vdcleanup(&dir);
612 vacfiledecref(parent);
613 return 0;
616 char *
617 rwstat(Fid *f)
619 if(!f->busy)
620 return vtstrdup(Enotexist);
621 return vtstrdup(Erdonly);
624 int
625 vdStat(VacFile *parent, VacDir *vd, uchar *p, int np)
627 char *ext;
628 int n, ret;
629 uvlong size;
630 Dir dir;
631 VacFile *vf;
633 memset(&dir, 0, sizeof(dir));
634 ext = nil;
636 /*
637 * Where do path and version come from
638 */
639 dir.qid.path = vd->qid;
640 dir.qid.vers = vd->mcount;
641 dir.mode = vd->mode & 0777;
642 if(vd->mode & ModeAppend){
643 dir.qid.type |= QTAPPEND;
644 dir.mode |= DMAPPEND;
646 if(vd->mode & ModeExclusive){
647 dir.qid.type |= QTEXCL;
648 dir.mode |= DMEXCL;
650 if(vd->mode & ModeDir){
651 dir.qid.type |= QTDIR;
652 dir.mode |= DMDIR;
655 if(vd->mode & (ModeLink|ModeDevice|ModeNamedPipe)){
656 vf = vacfilewalk(parent, vd->elem);
657 if(vf == nil)
658 return 0;
659 vacfilegetsize(vf, &size);
660 ext = malloc(size+1);
661 if(ext == nil)
662 return 0;
663 n = vacfileread(vf, ext, size, 0);
664 ext[size] = 0;
665 vacfiledecref(vf);
666 if(vd->mode & ModeLink){
667 dir.qid.type |= QTSYMLINK;
668 dir.mode |= DMSYMLINK;
670 if(vd->mode & ModeDevice)
671 dir.mode |= DMDEVICE;
672 if(vd->mode & ModeNamedPipe)
673 dir.mode |= DMNAMEDPIPE;
676 dir.atime = vd->atime;
677 dir.mtime = vd->mtime;
678 dir.length = vd->size;
680 dir.name = vd->elem;
681 dir.uid = vd->uid;
682 dir.gid = vd->gid;
683 dir.muid = vd->mid;
684 dir.ext = ext;
686 ret = convD2Mu(&dir, p, np, dotu);
687 free(ext);
688 return ret;
691 DirBuf*
692 dirBufAlloc(VacFile *vf)
694 DirBuf *db;
696 db = vtmallocz(sizeof(DirBuf));
697 db->vde = vdeopen(vf);
698 return db;
701 VacDir *
702 dirBufGet(DirBuf *db)
704 VacDir *vd;
705 int n;
707 if(db->eof)
708 return nil;
710 if(db->i >= db->n) {
711 n = vderead(db->vde, db->buf);
712 if(n < 0)
713 return nil;
714 db->i = 0;
715 db->n = n;
716 if(n == 0) {
717 db->eof = 1;
718 return nil;
722 vd = db->buf + db->i;
723 db->i++;
725 return vd;
728 int
729 dirBufUnget(DirBuf *db)
731 assert(db->i > 0);
732 db->i--;
733 return 1;
736 void
737 dirBufFree(DirBuf *db)
739 int i;
741 if(db == nil)
742 return;
744 for(i=db->i; i<db->n; i++)
745 vdcleanup(db->buf + i);
746 vdeclose(db->vde);
747 vtfree(db);
750 int
751 vacdirread(Fid *f, char *p, long off, long cnt)
753 int n, nb;
754 VacDir *vd;
756 /*
757 * special case of rewinding a directory
758 * otherwise ignore the offset
759 */
760 if(off == 0 && f->db) {
761 dirBufFree(f->db);
762 f->db = nil;
765 if(f->db == nil)
766 f->db = dirBufAlloc(f->file);
768 for(nb = 0; nb < cnt; nb += n) {
769 vd = dirBufGet(f->db);
770 if(vd == nil) {
771 if(!f->db->eof)
772 return -1;
773 break;
775 n = vdStat(f->file, vd, (uchar*)p, cnt-nb);
776 if(n <= BIT16SZ) {
777 dirBufUnget(f->db);
778 break;
780 vdcleanup(vd);
781 p += n;
783 return nb;
786 Fid *
787 newfid(int fid)
789 Fid *f, *ff;
791 ff = 0;
792 for(f = fids; f; f = f->next)
793 if(f->fid == fid)
794 return f;
795 else if(!ff && !f->busy)
796 ff = f;
797 if(ff){
798 ff->fid = fid;
799 return ff;
801 f = vtmallocz(sizeof *f);
802 f->fid = fid;
803 f->next = fids;
804 fids = f;
805 return f;
808 void
809 io(void)
811 char *err;
812 int n;
814 for(;;){
815 /*
816 * reading from a pipe or a network device
817 * will give an error after a few eof reads
818 * however, we cannot tell the difference
819 * between a zero-length read and an interrupt
820 * on the processes writing to us,
821 * so we wait for the error
822 */
823 n = read9pmsg(mfd[0], mdata, sizeof mdata);
824 if(n == 0)
825 continue;
826 if(n < 0)
827 break;
828 if(convM2Su(mdata, n, &rhdr, dotu) != n)
829 sysfatal("convM2S conversion error");
831 if(dflag)
832 fprint(2, "vacfs:<-%F\n", &rhdr);
834 thdr.data = (char*)mdata + IOHDRSZ;
835 if(!fcalls[rhdr.type])
836 err = "bad fcall type";
837 else
838 err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
839 if(err){
840 thdr.type = Rerror;
841 thdr.ename = err;
842 thdr.errornum = 0;
843 }else{
844 thdr.type = rhdr.type + 1;
845 thdr.fid = rhdr.fid;
847 thdr.tag = rhdr.tag;
848 if(dflag)
849 fprint(2, "vacfs:->%F\n", &thdr);
850 n = convS2Mu(&thdr, mdata, messagesize, dotu);
851 if(n <= BIT16SZ)
852 sysfatal("convS2Mu conversion error");
853 if(err)
854 vtfree(err);
856 if(write(mfd[1], mdata, n) != n)
857 sysfatal("mount write: %r");
861 int
862 permf(VacFile *vf, char *user, int p)
864 VacDir dir;
865 ulong perm;
867 if(vacfilegetdir(vf, &dir))
868 return 0;
869 perm = dir.mode & 0777;
871 if(noperm)
872 goto Good;
873 if((p*Pother) & perm)
874 goto Good;
875 if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm))
876 goto Good;
877 if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm))
878 goto Good;
879 vdcleanup(&dir);
880 return 0;
881 Good:
882 vdcleanup(&dir);
883 return 1;
886 int
887 perm(Fid *f, int p)
889 return permf(f->file, f->user, p);
892 void
893 init(char *file, char *host, long ncache, int readOnly)
895 notify(notifyf);
896 user = getuser();
898 conn = vtdial(host);
899 if(conn == nil)
900 sysfatal("could not connect to server: %r");
902 if(vtconnect(conn) < 0)
903 sysfatal("vtconnect: %r");
905 fs = vacfsopen(conn, file, /*readOnly ? ModeSnapshot :*/ VtOREAD, ncache);
906 if(fs == nil)
907 sysfatal("vfsOpen: %r");
910 void
911 vacshutdown(void)
913 Fid *f;
915 for(f = fids; f; f = f->next) {
916 if(!f->busy)
917 continue;
918 fprint(2, "open fid: %d\n", f->fid);
919 rclunk(f);
922 vacfsclose(fs);
923 vthangup(conn);