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;
685 dir.uidnum = atoi(vd->uid);
686 dir.gidnum = atoi(vd->gid);
688 ret = convD2Mu(&dir, p, np, dotu);
689 free(ext);
690 return ret;
693 DirBuf*
694 dirBufAlloc(VacFile *vf)
696 DirBuf *db;
698 db = vtmallocz(sizeof(DirBuf));
699 db->vde = vdeopen(vf);
700 return db;
703 VacDir *
704 dirBufGet(DirBuf *db)
706 VacDir *vd;
707 int n;
709 if(db->eof)
710 return nil;
712 if(db->i >= db->n) {
713 n = vderead(db->vde, db->buf);
714 if(n < 0)
715 return nil;
716 db->i = 0;
717 db->n = n;
718 if(n == 0) {
719 db->eof = 1;
720 return nil;
724 vd = db->buf + db->i;
725 db->i++;
727 return vd;
730 int
731 dirBufUnget(DirBuf *db)
733 assert(db->i > 0);
734 db->i--;
735 return 1;
738 void
739 dirBufFree(DirBuf *db)
741 int i;
743 if(db == nil)
744 return;
746 for(i=db->i; i<db->n; i++)
747 vdcleanup(db->buf + i);
748 vdeclose(db->vde);
749 vtfree(db);
752 int
753 vacdirread(Fid *f, char *p, long off, long cnt)
755 int n, nb;
756 VacDir *vd;
758 /*
759 * special case of rewinding a directory
760 * otherwise ignore the offset
761 */
762 if(off == 0 && f->db) {
763 dirBufFree(f->db);
764 f->db = nil;
767 if(f->db == nil)
768 f->db = dirBufAlloc(f->file);
770 for(nb = 0; nb < cnt; nb += n) {
771 vd = dirBufGet(f->db);
772 if(vd == nil) {
773 if(!f->db->eof)
774 return -1;
775 break;
777 n = vdStat(f->file, vd, (uchar*)p, cnt-nb);
778 if(n <= BIT16SZ) {
779 dirBufUnget(f->db);
780 break;
782 vdcleanup(vd);
783 p += n;
785 return nb;
788 Fid *
789 newfid(int fid)
791 Fid *f, *ff;
793 ff = 0;
794 for(f = fids; f; f = f->next)
795 if(f->fid == fid)
796 return f;
797 else if(!ff && !f->busy)
798 ff = f;
799 if(ff){
800 ff->fid = fid;
801 return ff;
803 f = vtmallocz(sizeof *f);
804 f->fid = fid;
805 f->next = fids;
806 fids = f;
807 return f;
810 void
811 io(void)
813 char *err;
814 int n;
816 for(;;){
817 /*
818 * reading from a pipe or a network device
819 * will give an error after a few eof reads
820 * however, we cannot tell the difference
821 * between a zero-length read and an interrupt
822 * on the processes writing to us,
823 * so we wait for the error
824 */
825 n = read9pmsg(mfd[0], mdata, sizeof mdata);
826 if(n == 0)
827 continue;
828 if(n < 0)
829 break;
830 if(convM2Su(mdata, n, &rhdr, dotu) != n)
831 sysfatal("convM2S conversion error");
833 if(dflag)
834 fprint(2, "vacfs:<-%F\n", &rhdr);
836 thdr.data = (char*)mdata + IOHDRSZ;
837 if(!fcalls[rhdr.type])
838 err = "bad fcall type";
839 else
840 err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
841 if(err){
842 thdr.type = Rerror;
843 thdr.ename = err;
844 thdr.errornum = 0;
845 }else{
846 thdr.type = rhdr.type + 1;
847 thdr.fid = rhdr.fid;
849 thdr.tag = rhdr.tag;
850 if(dflag)
851 fprint(2, "vacfs:->%F\n", &thdr);
852 n = convS2Mu(&thdr, mdata, messagesize, dotu);
853 if(n <= BIT16SZ)
854 sysfatal("convS2Mu conversion error");
855 if(err)
856 vtfree(err);
858 if(write(mfd[1], mdata, n) != n)
859 sysfatal("mount write: %r");
863 int
864 permf(VacFile *vf, char *user, int p)
866 VacDir dir;
867 ulong perm;
869 if(vacfilegetdir(vf, &dir))
870 return 0;
871 perm = dir.mode & 0777;
873 if(noperm)
874 goto Good;
875 if((p*Pother) & perm)
876 goto Good;
877 if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm))
878 goto Good;
879 if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm))
880 goto Good;
881 vdcleanup(&dir);
882 return 0;
883 Good:
884 vdcleanup(&dir);
885 return 1;
888 int
889 perm(Fid *f, int p)
891 return permf(f->file, f->user, p);
894 void
895 init(char *file, char *host, long ncache, int readOnly)
897 notify(notifyf);
898 user = getuser();
900 conn = vtdial(host);
901 if(conn == nil)
902 sysfatal("could not connect to server: %r");
904 if(vtconnect(conn) < 0)
905 sysfatal("vtconnect: %r");
907 fs = vacfsopen(conn, file, /*readOnly ? ModeSnapshot :*/ VtOREAD, ncache);
908 if(fs == nil)
909 sysfatal("vfsOpen: %r");
912 void
913 vacshutdown(void)
915 Fid *f;
917 for(f = fids; f; f = f->next) {
918 if(!f->busy)
919 continue;
920 fprint(2, "open fid: %d\n", f->fid);
921 rclunk(f);
924 vacfsclose(fs);
925 vthangup(conn);