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;
65 char *defmnt;
67 Fid * newfid(int);
68 void error(char*);
69 void io(void);
70 void vacshutdown(void);
71 void usage(void);
72 int perm(Fid*, int);
73 int permf(VacFile*, char*, int);
74 ulong getl(void *p);
75 void init(char*, char*, long, int);
76 DirBuf *dirBufAlloc(VacFile*);
77 VacDir *dirBufGet(DirBuf*);
78 int dirBufUnget(DirBuf*);
79 void dirBufFree(DirBuf*);
80 int vacdirread(Fid *f, char *p, long off, long cnt);
81 int vdStat(VacFile *parent, VacDir *vd, uchar *p, int np);
82 void srv(void* a);
85 char *rflush(Fid*), *rversion(Fid*),
86 *rauth(Fid*), *rattach(Fid*), *rwalk(Fid*),
87 *ropen(Fid*), *rcreate(Fid*),
88 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
89 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
91 char *(*fcalls[Tmax])(Fid*);
93 void
94 initfcalls(void)
95 {
96 fcalls[Tflush]= rflush;
97 fcalls[Tversion]= rversion;
98 fcalls[Tattach]= rattach;
99 fcalls[Tauth]= rauth;
100 fcalls[Twalk]= rwalk;
101 fcalls[Topen]= ropen;
102 fcalls[Tcreate]= rcreate;
103 fcalls[Tread]= rread;
104 fcalls[Twrite]= rwrite;
105 fcalls[Tclunk]= rclunk;
106 fcalls[Tremove]= rremove;
107 fcalls[Tstat]= rstat;
108 fcalls[Twstat]= rwstat;
111 char Eperm[] = "permission denied";
112 char Enotdir[] = "not a directory";
113 char Enotexist[] = "file does not exist";
114 char Einuse[] = "file in use";
115 char Eexist[] = "file exists";
116 char Enotowner[] = "not owner";
117 char Eisopen[] = "file already open for I/O";
118 char Excl[] = "exclusive use file already open";
119 char Ename[] = "illegal name";
120 char Erdonly[] = "read only file system";
121 char Eio[] = "i/o error";
122 char Eempty[] = "directory is not empty";
123 char Emode[] = "illegal mode";
125 int dflag;
127 void
128 notifyf(void *a, char *s)
130 USED(a);
131 if(strncmp(s, "interrupt", 9) == 0)
132 noted(NCONT);
133 noted(NDFLT);
136 void
137 threadmain(int argc, char *argv[])
139 char *defsrv, *q;
140 int p[2], l;
141 int stdio = 0;
142 char *host = nil;
143 long ncache = 1000;
144 int readOnly = 1;
146 fmtinstall('H', encodefmt);
147 fmtinstall('V', vtscorefmt);
148 fmtinstall('F', vtfcallfmt);
150 defsrv = nil;
151 ARGBEGIN{
152 case 'd':
153 fmtinstall('F', fcallfmt);
154 dflag = 1;
155 break;
156 case 'c':
157 ncache = atoi(EARGF(usage()));
158 break;
159 case 'i':
160 stdio = 1;
161 mfd[0] = 0;
162 mfd[1] = 1;
163 break;
164 case 'h':
165 host = EARGF(usage());
166 break;
167 case 's':
168 defsrv = EARGF(usage());
169 break;
170 case 'm':
171 defmnt = EARGF(usage());
172 break;
173 case 'p':
174 noperm = 1;
175 break;
176 case 'V':
177 chattyventi = 1;
178 break;
179 default:
180 usage();
181 }ARGEND
183 if(argc != 1)
184 usage();
186 initfcalls();
187 init(argv[0], host, ncache, readOnly);
189 if(pipe(p) < 0)
190 sysfatal("pipe failed: %r");
192 mfd[0] = p[0];
193 mfd[1] = p[0];
194 proccreate(srv, 0, 32 * 1024);
196 if(defsrv == nil && defmnt == nil){
197 q = strrchr(argv[0], '/');
198 if(q)
199 q++;
200 else
201 q = argv[0];
202 defsrv = vtmalloc(6+strlen(q)+1);
203 strcpy(defsrv, "vacfs.");
204 strcat(defsrv, q);
205 l = strlen(defsrv);
206 if(strcmp(defsrv+l-4, ".vac") == 0)
207 defsrv[l-4] = 0;
210 if(post9pservice(p[1], defsrv, defmnt) != 0)
211 sysfatal("post9pservice");
213 threadexits(0);
216 void
217 srv(void *a)
219 io();
220 vacshutdown();
223 void
224 usage(void)
226 fprint(2, "usage: %s [-sd] [-h host] [-c ncache] [-m mountpoint] vacfile\n", argv0);
227 threadexitsall("usage");
230 char*
231 rversion(Fid *unused)
233 Fid *f;
235 USED(unused);
237 for(f = fids; f; f = f->next)
238 if(f->busy)
239 rclunk(f);
241 if(rhdr.msize < 256)
242 return vtstrdup("version: message size too small");
243 messagesize = rhdr.msize;
244 if(messagesize > sizeof mdata)
245 messagesize = sizeof mdata;
246 thdr.msize = messagesize;
247 if(strncmp(rhdr.version, "9P2000", 6) != 0)
248 return vtstrdup("unrecognized 9P version");
249 thdr.version = "9P2000";
250 if(strncmp(rhdr.version, "9P2000.u", 8) == 0){
251 dotu = 1;
252 thdr.version = "9P2000.u";
254 return nil;
257 char*
258 rflush(Fid *f)
260 USED(f);
261 return 0;
264 char*
265 rauth(Fid *f)
267 USED(f);
268 return vtstrdup("vacfs: authentication not required");
271 char*
272 rattach(Fid *f)
274 /* no authentication for the momment */
275 VacFile *file;
276 char err[80];
278 file = vacfsgetroot(fs);
279 if(file == nil) {
280 rerrstr(err, sizeof err);
281 return vtstrdup(err);
284 f->busy = 1;
285 f->file = file;
286 f->qid.path = vacfilegetid(f->file);
287 f->qid.vers = 0;
288 f->qid.type = QTDIR;
289 thdr.qid = f->qid;
290 if(rhdr.uname[0])
291 f->user = vtstrdup(rhdr.uname);
292 else
293 f->user = "none";
294 return 0;
297 VacFile*
298 _vfWalk(VacFile *file, char *name)
300 VacFile *n;
302 n = vacfilewalk(file, name);
303 if(n)
304 return n;
305 if(strcmp(name, "SLASH") == 0)
306 return vacfilewalk(file, "/");
307 return nil;
310 char*
311 rwalk(Fid *f)
313 VacFile *file, *nfile;
314 Fid *nf;
315 int nqid, nwname;
316 Qid qid;
317 char *err = nil;
319 if(f->busy == 0)
320 return Enotexist;
321 nf = nil;
322 if(rhdr.fid != rhdr.newfid){
323 if(f->open)
324 return vtstrdup(Eisopen);
325 if(f->busy == 0)
326 return vtstrdup(Enotexist);
327 nf = newfid(rhdr.newfid);
328 if(nf->busy)
329 return vtstrdup(Eisopen);
330 nf->busy = 1;
331 nf->open = 0;
332 nf->qid = f->qid;
333 nf->file = vacfileincref(f->file);
334 nf->user = vtstrdup(f->user);
335 f = nf;
338 nwname = rhdr.nwname;
340 /* easy case */
341 if(nwname == 0) {
342 thdr.nwqid = 0;
343 return 0;
346 file = f->file;
347 vacfileincref(file);
348 qid = f->qid;
350 for(nqid = 0; nqid < nwname; nqid++){
351 if((qid.type & QTDIR) == 0){
352 err = Enotdir;
353 break;
355 if(!permf(file, f->user, Pexec)) {
356 err = Eperm;
357 break;
359 nfile = _vfWalk(file, rhdr.wname[nqid]);
360 if(nfile == nil)
361 break;
362 vacfiledecref(file);
363 file = nfile;
364 qid.type = QTFILE;
365 if(vacfileisdir(file))
366 qid.type = QTDIR;
367 if(vacfilegetmode(file)&ModeLink)
368 qid.type = QTSYMLINK;
369 qid.vers = vacfilegetmcount(file);
370 qid.path = vacfilegetid(file);
371 thdr.wqid[nqid] = qid;
374 thdr.nwqid = nqid;
376 if(nqid == nwname){
377 /* success */
378 f->qid = thdr.wqid[nqid-1];
379 vacfiledecref(f->file);
380 f->file = file;
381 return 0;
384 vacfiledecref(file);
385 if(nf != nil)
386 rclunk(nf);
388 /* only error on the first element */
389 if(nqid == 0)
390 return vtstrdup(err);
392 return 0;
395 char *
396 ropen(Fid *f)
398 int mode, trunc;
400 if(f->open)
401 return vtstrdup(Eisopen);
402 if(!f->busy)
403 return vtstrdup(Enotexist);
405 mode = rhdr.mode;
406 thdr.iounit = messagesize - IOHDRSZ;
407 if(f->qid.type & QTDIR){
408 if(mode != OREAD)
409 return vtstrdup(Eperm);
410 if(!perm(f, Pread))
411 return vtstrdup(Eperm);
412 thdr.qid = f->qid;
413 f->db = nil;
414 f->open = 1;
415 return 0;
417 if(mode & ORCLOSE)
418 return vtstrdup(Erdonly);
419 trunc = mode & OTRUNC;
420 mode &= OPERM;
421 if(mode==OWRITE || mode==ORDWR || trunc)
422 if(!perm(f, Pwrite))
423 return vtstrdup(Eperm);
424 if(mode==OREAD || mode==ORDWR)
425 if(!perm(f, Pread))
426 return vtstrdup(Eperm);
427 if(mode==OEXEC)
428 if(!perm(f, Pexec))
429 return vtstrdup(Eperm);
430 thdr.qid = f->qid;
431 thdr.iounit = messagesize - IOHDRSZ;
432 f->open = 1;
433 return 0;
436 char*
437 rcreate(Fid* fid)
439 VacFile *vf;
440 ulong mode;
442 if(fid->open)
443 return vtstrdup(Eisopen);
444 if(!fid->busy)
445 return vtstrdup(Enotexist);
446 if(fs->mode & ModeSnapshot)
447 return vtstrdup(Erdonly);
448 vf = fid->file;
449 if(!vacfileisdir(vf))
450 return vtstrdup(Enotdir);
451 if(!permf(vf, fid->user, Pwrite))
452 return vtstrdup(Eperm);
454 mode = rhdr.perm & 0777;
456 if(rhdr.perm & DMDIR){
457 if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))
458 return vtstrdup(Emode);
459 switch(rhdr.mode & OPERM){
460 default:
461 return vtstrdup(Emode);
462 case OEXEC:
463 case OREAD:
464 break;
465 case OWRITE:
466 case ORDWR:
467 return vtstrdup(Eperm);
469 mode |= ModeDir;
471 vf = vacfilecreate(vf, rhdr.name, mode, "none");
472 if(vf == nil) {
473 char err[80];
474 rerrstr(err, sizeof err);
476 return vtstrdup(err);
479 vacfiledecref(fid->file);
481 fid->file = vf;
482 fid->qid.type = QTFILE;
483 if(vacfileisdir(vf))
484 fid->qid.type = QTDIR;
485 fid->qid.vers = vacfilegetmcount(vf);
486 fid->qid.path = vacfilegetid(vf);
488 thdr.qid = fid->qid;
489 thdr.iounit = messagesize - IOHDRSZ;
491 return 0;
494 char*
495 rread(Fid *f)
497 char *buf;
498 vlong off;
499 int cnt;
500 VacFile *vf;
501 char err[80];
502 int n;
504 if(!f->busy)
505 return vtstrdup(Enotexist);
506 vf = f->file;
507 thdr.count = 0;
508 off = rhdr.offset;
509 buf = thdr.data;
510 cnt = rhdr.count;
511 if(f->qid.type & QTDIR)
512 n = vacdirread(f, buf, off, cnt);
513 else if(vacfilegetmode(f->file)&ModeDevice)
514 return vtstrdup("device");
515 else if(vacfilegetmode(f->file)&ModeLink)
516 return vtstrdup("symbolic link");
517 else if(vacfilegetmode(f->file)&ModeNamedPipe)
518 return vtstrdup("named pipe");
519 else
520 n = vacfileread(vf, buf, cnt, off);
521 if(n < 0) {
522 rerrstr(err, sizeof err);
523 return vtstrdup(err);
525 thdr.count = n;
526 return 0;
529 char*
530 rwrite(Fid *f)
532 char *buf;
533 vlong off;
534 int cnt;
535 VacFile *vf;
537 if(!f->busy)
538 return vtstrdup(Enotexist);
539 vf = f->file;
540 thdr.count = 0;
541 off = rhdr.offset;
542 buf = rhdr.data;
543 cnt = rhdr.count;
544 if(f->qid.type & QTDIR)
545 return "file is a directory";
546 thdr.count = vacfilewrite(vf, buf, cnt, off, "none");
547 if(thdr.count < 0) {
548 char err[80];
550 rerrstr(err, sizeof err);
551 fprint(2, "write failed: %s\n", err);
552 return vtstrdup(err);
554 return 0;
557 char *
558 rclunk(Fid *f)
560 f->busy = 0;
561 f->open = 0;
562 vtfree(f->user);
563 f->user = nil;
564 if(f->file)
565 vacfiledecref(f->file);
566 f->file = nil;
567 dirBufFree(f->db);
568 f->db = nil;
569 return 0;
572 char *
573 rremove(Fid *f)
575 VacFile *vf, *vfp;
576 char errbuf[80];
577 char *err = nil;
579 if(!f->busy)
580 return vtstrdup(Enotexist);
581 vf = f->file;
582 vfp = vacfilegetparent(vf);
584 if(!permf(vfp, f->user, Pwrite)) {
585 err = Eperm;
586 goto Exit;
589 if(!vacfileremove(vf, "none")) {
590 rerrstr(errbuf, sizeof errbuf);
591 print("vfRemove failed: %s\n", errbuf);
593 err = errbuf;
596 Exit:
597 vacfiledecref(vfp);
598 rclunk(f);
599 return vtstrdup(err);
602 char *
603 rstat(Fid *f)
605 VacDir dir;
606 static uchar statbuf[1024];
607 VacFile *parent;
609 if(!f->busy)
610 return vtstrdup(Enotexist);
611 parent = vacfilegetparent(f->file);
612 vacfilegetdir(f->file, &dir);
613 thdr.stat = statbuf;
614 thdr.nstat = vdStat(parent, &dir, thdr.stat, sizeof statbuf);
615 vdcleanup(&dir);
616 vacfiledecref(parent);
617 return 0;
620 char *
621 rwstat(Fid *f)
623 if(!f->busy)
624 return vtstrdup(Enotexist);
625 return vtstrdup(Erdonly);
628 int
629 vdStat(VacFile *parent, VacDir *vd, uchar *p, int np)
631 char *ext;
632 int n, ret;
633 uvlong size;
634 Dir dir;
635 VacFile *vf;
637 memset(&dir, 0, sizeof(dir));
638 ext = nil;
640 /*
641 * Where do path and version come from
642 */
643 dir.qid.path = vd->qid;
644 dir.qid.vers = vd->mcount;
645 dir.mode = vd->mode & 0777;
646 if(vd->mode & ModeAppend){
647 dir.qid.type |= QTAPPEND;
648 dir.mode |= DMAPPEND;
650 if(vd->mode & ModeExclusive){
651 dir.qid.type |= QTEXCL;
652 dir.mode |= DMEXCL;
654 if(vd->mode & ModeDir){
655 dir.qid.type |= QTDIR;
656 dir.mode |= DMDIR;
659 if(vd->mode & (ModeLink|ModeDevice|ModeNamedPipe)){
660 vf = vacfilewalk(parent, vd->elem);
661 if(vf == nil)
662 return 0;
663 vacfilegetsize(vf, &size);
664 ext = malloc(size+1);
665 if(ext == nil)
666 return 0;
667 n = vacfileread(vf, ext, size, 0);
668 ext[size] = 0;
669 vacfiledecref(vf);
670 if(vd->mode & ModeLink){
671 dir.qid.type |= QTSYMLINK;
672 dir.mode |= DMSYMLINK;
674 if(vd->mode & ModeDevice)
675 dir.mode |= DMDEVICE;
676 if(vd->mode & ModeNamedPipe)
677 dir.mode |= DMNAMEDPIPE;
680 dir.atime = vd->atime;
681 dir.mtime = vd->mtime;
682 dir.length = vd->size;
684 dir.name = vd->elem;
685 dir.uid = vd->uid;
686 dir.gid = vd->gid;
687 dir.muid = vd->mid;
688 dir.ext = ext;
689 dir.uidnum = atoi(vd->uid);
690 dir.gidnum = atoi(vd->gid);
692 ret = convD2Mu(&dir, p, np, dotu);
693 free(ext);
694 return ret;
697 DirBuf*
698 dirBufAlloc(VacFile *vf)
700 DirBuf *db;
702 db = vtmallocz(sizeof(DirBuf));
703 db->vde = vdeopen(vf);
704 return db;
707 VacDir *
708 dirBufGet(DirBuf *db)
710 VacDir *vd;
711 int n;
713 if(db->eof)
714 return nil;
716 if(db->i >= db->n) {
717 n = vderead(db->vde, db->buf);
718 if(n < 0)
719 return nil;
720 db->i = 0;
721 db->n = n;
722 if(n == 0) {
723 db->eof = 1;
724 return nil;
728 vd = db->buf + db->i;
729 db->i++;
731 return vd;
734 int
735 dirBufUnget(DirBuf *db)
737 assert(db->i > 0);
738 db->i--;
739 return 1;
742 void
743 dirBufFree(DirBuf *db)
745 int i;
747 if(db == nil)
748 return;
750 for(i=db->i; i<db->n; i++)
751 vdcleanup(db->buf + i);
752 vdeclose(db->vde);
753 vtfree(db);
756 int
757 vacdirread(Fid *f, char *p, long off, long cnt)
759 int n, nb;
760 VacDir *vd;
762 /*
763 * special case of rewinding a directory
764 * otherwise ignore the offset
765 */
766 if(off == 0 && f->db) {
767 dirBufFree(f->db);
768 f->db = nil;
771 if(f->db == nil)
772 f->db = dirBufAlloc(f->file);
774 for(nb = 0; nb < cnt; nb += n) {
775 vd = dirBufGet(f->db);
776 if(vd == nil) {
777 if(!f->db->eof)
778 return -1;
779 break;
781 n = vdStat(f->file, vd, (uchar*)p, cnt-nb);
782 if(n <= BIT16SZ) {
783 dirBufUnget(f->db);
784 break;
786 vdcleanup(vd);
787 p += n;
789 return nb;
792 Fid *
793 newfid(int fid)
795 Fid *f, *ff;
797 ff = 0;
798 for(f = fids; f; f = f->next)
799 if(f->fid == fid)
800 return f;
801 else if(!ff && !f->busy)
802 ff = f;
803 if(ff){
804 ff->fid = fid;
805 return ff;
807 f = vtmallocz(sizeof *f);
808 f->fid = fid;
809 f->next = fids;
810 fids = f;
811 return f;
814 void
815 io(void)
817 char *err;
818 int n;
820 for(;;){
821 n = read9pmsg(mfd[0], mdata, sizeof mdata);
822 if(n <= 0)
823 break;
824 if(convM2Su(mdata, n, &rhdr, dotu) != n)
825 sysfatal("convM2S conversion error");
827 if(dflag)
828 fprint(2, "vacfs:<-%F\n", &rhdr);
830 thdr.data = (char*)mdata + IOHDRSZ;
831 if(!fcalls[rhdr.type])
832 err = "bad fcall type";
833 else
834 err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
835 if(err){
836 thdr.type = Rerror;
837 thdr.ename = err;
838 thdr.errornum = 0;
839 }else{
840 thdr.type = rhdr.type + 1;
841 thdr.fid = rhdr.fid;
843 thdr.tag = rhdr.tag;
844 if(dflag)
845 fprint(2, "vacfs:->%F\n", &thdr);
846 n = convS2Mu(&thdr, mdata, messagesize, dotu);
847 if(n <= BIT16SZ)
848 sysfatal("convS2Mu conversion error");
849 if(err)
850 vtfree(err);
852 if(write(mfd[1], mdata, n) != n)
853 sysfatal("mount write: %r");
857 int
858 permf(VacFile *vf, char *user, int p)
860 VacDir dir;
861 ulong perm;
863 if(vacfilegetdir(vf, &dir))
864 return 0;
865 perm = dir.mode & 0777;
867 if(noperm)
868 goto Good;
869 if((p*Pother) & perm)
870 goto Good;
871 if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm))
872 goto Good;
873 if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm))
874 goto Good;
875 vdcleanup(&dir);
876 return 0;
877 Good:
878 vdcleanup(&dir);
879 return 1;
882 int
883 perm(Fid *f, int p)
885 return permf(f->file, f->user, p);
888 void
889 init(char *file, char *host, long ncache, int readOnly)
891 notify(notifyf);
892 user = getuser();
894 conn = vtdial(host);
895 if(conn == nil)
896 sysfatal("could not connect to server: %r");
898 if(vtconnect(conn) < 0)
899 sysfatal("vtconnect: %r");
901 fs = vacfsopen(conn, file, /*readOnly ? ModeSnapshot :*/ VtOREAD, ncache);
902 if(fs == nil)
903 sysfatal("vfsOpen: %r");
906 void
907 vacshutdown(void)
909 Fid *f;
911 for(f = fids; f; f = f->next) {
912 if(!f->busy)
913 continue;
914 rclunk(f);
917 vacfsclose(fs);
918 vthangup(conn);