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;
65 Fid * newfid(int);
66 void error(char*);
67 void io(void);
68 void vacshutdown(void);
69 void usage(void);
70 int perm(Fid*, int);
71 int permf(VacFile*, char*, int);
72 ulong getl(void *p);
73 void init(char*, char*, long, int);
74 DirBuf *dirBufAlloc(VacFile*);
75 VacDir *dirBufGet(DirBuf*);
76 int dirBufUnget(DirBuf*);
77 void dirBufFree(DirBuf*);
78 int vacdirread(Fid *f, char *p, long off, long cnt);
79 int vdStat(VacDir *vd, uchar *p, int np);
80 void srv(void* a);
83 char *rflush(Fid*), *rversion(Fid*),
84 *rauth(Fid*), *rattach(Fid*), *rwalk(Fid*),
85 *ropen(Fid*), *rcreate(Fid*),
86 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
87 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
89 char *(*fcalls[Tmax])(Fid*);
91 void
92 initfcalls(void)
93 {
94 fcalls[Tflush]= rflush;
95 fcalls[Tversion]= rversion;
96 fcalls[Tattach]= rattach;
97 fcalls[Tauth]= rauth;
98 fcalls[Twalk]= rwalk;
99 fcalls[Topen]= ropen;
100 fcalls[Tcreate]= rcreate;
101 fcalls[Tread]= rread;
102 fcalls[Twrite]= rwrite;
103 fcalls[Tclunk]= rclunk;
104 fcalls[Tremove]= rremove;
105 fcalls[Tstat]= rstat;
106 fcalls[Twstat]= rwstat;
107 };
109 char Eperm[] = "permission denied";
110 char Enotdir[] = "not a directory";
111 char Enotexist[] = "file does not exist";
112 char Einuse[] = "file in use";
113 char Eexist[] = "file exists";
114 char Enotowner[] = "not owner";
115 char Eisopen[] = "file already open for I/O";
116 char Excl[] = "exclusive use file already open";
117 char Ename[] = "illegal name";
118 char Erdonly[] = "read only file system";
119 char Eio[] = "i/o error";
120 char Eempty[] = "directory is not empty";
121 char Emode[] = "illegal mode";
123 int dflag;
125 void
126 notifyf(void *a, char *s)
128 USED(a);
129 if(strncmp(s, "interrupt", 9) == 0)
130 noted(NCONT);
131 noted(NDFLT);
134 void
135 threadmain(int argc, char *argv[])
137 char *defsrv, *q;
138 int p[2], l;
139 int stdio = 0;
140 char *host = nil;
141 long ncache = 1000;
142 int readOnly = 1;
144 defsrv = nil;
145 ARGBEGIN{
146 case 'd':
147 fmtinstall('F', fcallfmt);
148 dflag = 1;
149 break;
150 case 'c':
151 ncache = atoi(EARGF(usage()));
152 break;
153 case 'i':
154 stdio = 1;
155 mfd[0] = 0;
156 mfd[1] = 1;
157 break;
158 case 'h':
159 host = EARGF(usage());
160 break;
161 case 's':
162 defsrv = EARGF(usage());
163 break;
164 case 'p':
165 noperm = 1;
166 break;
167 default:
168 usage();
169 }ARGEND
171 if(argc != 1)
172 usage();
174 initfcalls();
175 init(argv[0], host, ncache, readOnly);
177 if(pipe(p) < 0)
178 sysfatal("pipe failed: %r");
180 mfd[0] = p[0];
181 mfd[1] = p[0];
182 proccreate(srv, 0, 32 * 1024);
184 if(defsrv == nil){
185 q = strrchr(argv[0], '/');
186 if(q)
187 q++;
188 else
189 q = argv[0];
190 defsrv = vtmalloc(6+strlen(q)+1);
191 strcpy(defsrv, "vacfs.");
192 strcat(defsrv, q);
193 l = strlen(defsrv);
194 if(strcmp(defsrv+l-4, ".vac") == 0)
195 defsrv[l-4] = 0;
198 if(post9pservice(p[1], defsrv) != 0)
199 sysfatal("post9pservice");
202 threadexits(0);
205 void
206 srv(void *a)
208 io();
209 vacshutdown();
212 void
213 usage(void)
215 fprint(2, "usage: %s [-sd] [-h host] [-c ncache] [-m mountpoint] vacfile\n", argv0);
216 threadexitsall("usage");
219 char*
220 rversion(Fid *unused)
222 Fid *f;
224 USED(unused);
226 for(f = fids; f; f = f->next)
227 if(f->busy)
228 rclunk(f);
230 if(rhdr.msize < 256)
231 return vtstrdup("version: message size too small");
232 messagesize = rhdr.msize;
233 if(messagesize > sizeof mdata)
234 messagesize = sizeof mdata;
235 thdr.msize = messagesize;
236 if(strncmp(rhdr.version, "9P2000", 6) != 0)
237 return vtstrdup("unrecognized 9P version");
238 thdr.version = "9P2000";
239 return nil;
242 char*
243 rflush(Fid *f)
245 USED(f);
246 return 0;
249 char*
250 rauth(Fid *f)
252 USED(f);
253 return vtstrdup("vacfs: authentication not required");
256 char*
257 rattach(Fid *f)
259 /* no authentication for the momment */
260 VacFile *file;
261 char err[80];
263 file = vacfsgetroot(fs);
264 if(file == nil) {
265 rerrstr(err, sizeof err);
266 return vtstrdup(err);
269 f->busy = 1;
270 f->file = file;
271 f->qid.path = vacfilegetid(f->file);
272 f->qid.vers = 0;
273 f->qid.type = QTDIR;
274 thdr.qid = f->qid;
275 if(rhdr.uname[0])
276 f->user = vtstrdup(rhdr.uname);
277 else
278 f->user = "none";
279 return 0;
282 VacFile*
283 _vfWalk(VacFile *file, char *name)
285 VacFile *n;
287 n = vacfilewalk(file, name);
288 if(n)
289 return n;
290 if(strcmp(name, "SLASH") == 0)
291 return vacfilewalk(file, "/");
292 return nil;
295 char*
296 rwalk(Fid *f)
298 VacFile *file, *nfile;
299 Fid *nf;
300 int nqid, nwname;
301 Qid qid;
302 char *err = nil;
304 if(f->busy == 0)
305 return Enotexist;
306 nf = nil;
307 if(rhdr.fid != rhdr.newfid){
308 if(f->open)
309 return vtstrdup(Eisopen);
310 if(f->busy == 0)
311 return vtstrdup(Enotexist);
312 nf = newfid(rhdr.newfid);
313 if(nf->busy)
314 return vtstrdup(Eisopen);
315 nf->busy = 1;
316 nf->open = 0;
317 nf->qid = f->qid;
318 nf->file = vacfileincref(f->file);
319 nf->user = vtstrdup(f->user);
320 f = nf;
323 nwname = rhdr.nwname;
325 /* easy case */
326 if(nwname == 0) {
327 thdr.nwqid = 0;
328 return 0;
331 file = f->file;
332 vacfileincref(file);
333 qid = f->qid;
335 for(nqid = 0; nqid < nwname; nqid++){
336 if((qid.type & QTDIR) == 0){
337 err = Enotdir;
338 break;
340 if(!permf(file, f->user, Pexec)) {
341 err = Eperm;
342 break;
344 nfile = _vfWalk(file, rhdr.wname[nqid]);
345 if(nfile == nil)
346 break;
347 vacfiledecref(file);
348 file = nfile;
349 qid.type = QTFILE;
350 if(vacfileisdir(file))
351 qid.type = QTDIR;
352 qid.vers = vacfilegetmcount(file);
353 qid.path = vacfilegetid(file);
354 thdr.wqid[nqid] = qid;
357 thdr.nwqid = nqid;
359 if(nqid == nwname){
360 /* success */
361 f->qid = thdr.wqid[nqid-1];
362 vacfiledecref(f->file);
363 f->file = file;
364 return 0;
367 vacfiledecref(file);
368 if(nf != nil)
369 rclunk(nf);
371 /* only error on the first element */
372 if(nqid == 0)
373 return vtstrdup(err);
375 return 0;
378 char *
379 ropen(Fid *f)
381 int mode, trunc;
383 if(f->open)
384 return vtstrdup(Eisopen);
385 if(!f->busy)
386 return vtstrdup(Enotexist);
388 mode = rhdr.mode;
389 thdr.iounit = messagesize - IOHDRSZ;
390 if(f->qid.type & QTDIR){
391 if(mode != OREAD)
392 return vtstrdup(Eperm);
393 if(!perm(f, Pread))
394 return vtstrdup(Eperm);
395 thdr.qid = f->qid;
396 f->db = nil;
397 f->open = 1;
398 return 0;
400 if(mode & ORCLOSE)
401 return vtstrdup(Erdonly);
402 trunc = mode & OTRUNC;
403 mode &= OPERM;
404 if(mode==OWRITE || mode==ORDWR || trunc)
405 if(!perm(f, Pwrite))
406 return vtstrdup(Eperm);
407 if(mode==OREAD || mode==ORDWR)
408 if(!perm(f, Pread))
409 return vtstrdup(Eperm);
410 if(mode==OEXEC)
411 if(!perm(f, Pexec))
412 return vtstrdup(Eperm);
413 thdr.qid = f->qid;
414 thdr.iounit = messagesize - IOHDRSZ;
415 f->open = 1;
416 return 0;
419 char*
420 rcreate(Fid* fid)
422 VacFile *vf;
423 ulong mode;
425 if(fid->open)
426 return vtstrdup(Eisopen);
427 if(!fid->busy)
428 return vtstrdup(Enotexist);
429 if(fs->mode & ModeSnapshot)
430 return vtstrdup(Erdonly);
431 vf = fid->file;
432 if(!vacfileisdir(vf))
433 return vtstrdup(Enotdir);
434 if(!permf(vf, fid->user, Pwrite))
435 return vtstrdup(Eperm);
437 mode = rhdr.perm & 0777;
439 if(rhdr.perm & DMDIR){
440 if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))
441 return vtstrdup(Emode);
442 switch(rhdr.mode & OPERM){
443 default:
444 return vtstrdup(Emode);
445 case OEXEC:
446 case OREAD:
447 break;
448 case OWRITE:
449 case ORDWR:
450 return vtstrdup(Eperm);
452 mode |= ModeDir;
454 vf = vacfilecreate(vf, rhdr.name, mode, "none");
455 if(vf == nil) {
456 char err[80];
457 rerrstr(err, sizeof err);
459 return vtstrdup(err);
462 vacfiledecref(fid->file);
464 fid->file = vf;
465 fid->qid.type = QTFILE;
466 if(vacfileisdir(vf))
467 fid->qid.type = QTDIR;
468 fid->qid.vers = vacfilegetmcount(vf);
469 fid->qid.path = vacfilegetid(vf);
471 thdr.qid = fid->qid;
472 thdr.iounit = messagesize - IOHDRSZ;
474 return 0;
477 char*
478 rread(Fid *f)
480 char *buf;
481 vlong off;
482 int cnt;
483 VacFile *vf;
484 char err[80];
485 int n;
487 if(!f->busy)
488 return vtstrdup(Enotexist);
489 vf = f->file;
490 thdr.count = 0;
491 off = rhdr.offset;
492 buf = thdr.data;
493 cnt = rhdr.count;
494 if(f->qid.type & QTDIR)
495 n = vacdirread(f, buf, off, cnt);
496 else
497 n = vacfileread(vf, buf, cnt, off);
498 if(n < 0) {
499 rerrstr(err, sizeof err);
500 return vtstrdup(err);
502 thdr.count = n;
503 return 0;
506 char*
507 rwrite(Fid *f)
509 char *buf;
510 vlong off;
511 int cnt;
512 VacFile *vf;
514 if(!f->busy)
515 return vtstrdup(Enotexist);
516 vf = f->file;
517 thdr.count = 0;
518 off = rhdr.offset;
519 buf = rhdr.data;
520 cnt = rhdr.count;
521 if(f->qid.type & QTDIR)
522 return "file is a directory";
523 thdr.count = vacfilewrite(vf, buf, cnt, off, "none");
524 if(thdr.count < 0) {
525 char err[80];
527 rerrstr(err, sizeof err);
528 fprint(2, "write failed: %s\n", err);
529 return vtstrdup(err);
531 return 0;
534 char *
535 rclunk(Fid *f)
537 f->busy = 0;
538 f->open = 0;
539 vtfree(f->user);
540 f->user = nil;
541 vacfiledecref(f->file);
542 f->file = nil;
543 dirBufFree(f->db);
544 f->db = nil;
545 return 0;
548 char *
549 rremove(Fid *f)
551 VacFile *vf, *vfp;
552 char errbuf[80];
553 char *err = nil;
555 if(!f->busy)
556 return vtstrdup(Enotexist);
557 vf = f->file;
558 vfp = vacfilegetparent(vf);
560 if(!permf(vfp, f->user, Pwrite)) {
561 err = Eperm;
562 goto Exit;
565 if(!vacfileremove(vf, "none")) {
566 rerrstr(errbuf, sizeof errbuf);
567 print("vfRemove failed: %s\n", errbuf);
569 err = errbuf;
572 Exit:
573 vacfiledecref(vfp);
574 rclunk(f);
575 return vtstrdup(err);
578 char *
579 rstat(Fid *f)
581 VacDir dir;
582 static uchar statbuf[1024];
584 if(!f->busy)
585 return vtstrdup(Enotexist);
586 vacfilegetdir(f->file, &dir);
587 thdr.stat = statbuf;
588 thdr.nstat = vdStat(&dir, thdr.stat, sizeof statbuf);
589 vdcleanup(&dir);
590 return 0;
593 char *
594 rwstat(Fid *f)
596 if(!f->busy)
597 return vtstrdup(Enotexist);
598 return vtstrdup(Erdonly);
601 int
602 vdStat(VacDir *vd, uchar *p, int np)
604 Dir dir;
606 memset(&dir, 0, sizeof(dir));
608 /*
609 * Where do path and version come from
610 */
611 dir.qid.path = vd->qid;
612 dir.qid.vers = vd->mcount;
613 dir.mode = vd->mode & 0777;
614 if(vd->mode & ModeAppend){
615 dir.qid.type |= QTAPPEND;
616 dir.mode |= DMAPPEND;
618 if(vd->mode & ModeExclusive){
619 dir.qid.type |= QTEXCL;
620 dir.mode |= DMEXCL;
622 if(vd->mode & ModeDir){
623 dir.qid.type |= QTDIR;
624 dir.mode |= DMDIR;
627 dir.atime = vd->atime;
628 dir.mtime = vd->mtime;
629 dir.length = vd->size;
631 dir.name = vd->elem;
632 dir.uid = vd->uid;
633 dir.gid = vd->gid;
634 dir.muid = vd->mid;
636 return convD2M(&dir, p, np);
639 DirBuf*
640 dirBufAlloc(VacFile *vf)
642 DirBuf *db;
644 db = vtmallocz(sizeof(DirBuf));
645 db->vde = vdeopen(vf);
646 return db;
649 VacDir *
650 dirBufGet(DirBuf *db)
652 VacDir *vd;
653 int n;
655 if(db->eof)
656 return nil;
658 if(db->i >= db->n) {
659 n = vderead(db->vde, db->buf);
660 if(n < 0)
661 return nil;
662 db->i = 0;
663 db->n = n;
664 if(n == 0) {
665 db->eof = 1;
666 return nil;
670 vd = db->buf + db->i;
671 db->i++;
673 return vd;
676 int
677 dirBufUnget(DirBuf *db)
679 assert(db->i > 0);
680 db->i--;
681 return 1;
684 void
685 dirBufFree(DirBuf *db)
687 int i;
689 if(db == nil)
690 return;
692 for(i=db->i; i<db->n; i++)
693 vdcleanup(db->buf + i);
694 vdeclose(db->vde);
695 vtfree(db);
698 int
699 vacdirread(Fid *f, char *p, long off, long cnt)
701 int n, nb;
702 VacDir *vd;
704 /*
705 * special case of rewinding a directory
706 * otherwise ignore the offset
707 */
708 if(off == 0 && f->db) {
709 dirBufFree(f->db);
710 f->db = nil;
713 if(f->db == nil)
714 f->db = dirBufAlloc(f->file);
716 for(nb = 0; nb < cnt; nb += n) {
717 vd = dirBufGet(f->db);
718 if(vd == nil) {
719 if(!f->db->eof)
720 return -1;
721 break;
723 n = vdStat(vd, (uchar*)p, cnt-nb);
724 if(n <= BIT16SZ) {
725 dirBufUnget(f->db);
726 break;
728 vdcleanup(vd);
729 p += n;
731 return nb;
734 Fid *
735 newfid(int fid)
737 Fid *f, *ff;
739 ff = 0;
740 for(f = fids; f; f = f->next)
741 if(f->fid == fid)
742 return f;
743 else if(!ff && !f->busy)
744 ff = f;
745 if(ff){
746 ff->fid = fid;
747 return ff;
749 f = vtmallocz(sizeof *f);
750 f->fid = fid;
751 f->next = fids;
752 fids = f;
753 return f;
756 void
757 io(void)
759 char *err;
760 int n;
762 for(;;){
763 /*
764 * reading from a pipe or a network device
765 * will give an error after a few eof reads
766 * however, we cannot tell the difference
767 * between a zero-length read and an interrupt
768 * on the processes writing to us,
769 * so we wait for the error
770 */
771 n = read9pmsg(mfd[0], mdata, sizeof mdata);
772 if(n == 0)
773 continue;
774 if(n < 0)
775 break;
776 if(convM2S(mdata, n, &rhdr) != n)
777 sysfatal("convM2S conversion error");
779 if(dflag)
780 fprint(2, "vacfs:<-%F\n", &rhdr);
782 thdr.data = (char*)mdata + IOHDRSZ;
783 if(!fcalls[rhdr.type])
784 err = "bad fcall type";
785 else
786 err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
787 if(err){
788 thdr.type = Rerror;
789 thdr.ename = err;
790 }else{
791 thdr.type = rhdr.type + 1;
792 thdr.fid = rhdr.fid;
794 thdr.tag = rhdr.tag;
795 if(dflag)
796 fprint(2, "vacfs:->%F\n", &thdr);
797 n = convS2M(&thdr, mdata, messagesize);
798 if (err)
799 vtfree(err);
801 if(write(mfd[1], mdata, n) != n)
802 sysfatal("mount write: %r");
806 int
807 permf(VacFile *vf, char *user, int p)
809 VacDir dir;
810 ulong perm;
812 if(vacfilegetdir(vf, &dir))
813 return 0;
814 perm = dir.mode & 0777;
816 if(noperm)
817 goto Good;
818 if((p*Pother) & perm)
819 goto Good;
820 if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm))
821 goto Good;
822 if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm))
823 goto Good;
824 vdcleanup(&dir);
825 return 0;
826 Good:
827 vdcleanup(&dir);
828 return 1;
831 int
832 perm(Fid *f, int p)
834 return permf(f->file, f->user, p);
837 void
838 init(char *file, char *host, long ncache, int readOnly)
840 notify(notifyf);
841 user = getuser();
843 fmtinstall('V', vtscorefmt);
844 // fmtinstall('R', vtErrFmt);
846 conn = vtdial(host);
847 if(conn == nil)
848 sysfatal("could not connect to server: %r");
850 if(vtconnect(conn) < 0)
851 sysfatal("vtconnect: %r");
853 fs = vacfsopen(conn, file, /*readOnly ? ModeSnapshot :*/ VtOREAD, ncache);
854 if(fs == nil)
855 sysfatal("vfsOpen: %r");
858 void
859 vacshutdown(void)
861 Fid *f;
863 for(f = fids; f; f = f->next) {
864 if(!f->busy)
865 continue;
866 fprint(2, "open fid: %d\n", f->fid);
867 rclunk(f);
870 vacfsclose(fs);
871 vthangup(conn);