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 shutdown(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[])(Fid*) = {
90 [Tflush] rflush,
91 [Tversion] rversion,
92 [Tattach] rattach,
93 [Tauth] rauth,
94 [Twalk] rwalk,
95 [Topen] ropen,
96 [Tcreate] rcreate,
97 [Tread] rread,
98 [Twrite] rwrite,
99 [Tclunk] rclunk,
100 [Tremove] rremove,
101 [Tstat] rstat,
102 [Twstat] rwstat,
103 };
105 char Eperm[] = "permission denied";
106 char Enotdir[] = "not a directory";
107 char Enotexist[] = "file does not exist";
108 char Einuse[] = "file in use";
109 char Eexist[] = "file exists";
110 char Enotowner[] = "not owner";
111 char Eisopen[] = "file already open for I/O";
112 char Excl[] = "exclusive use file already open";
113 char Ename[] = "illegal name";
114 char Erdonly[] = "read only file system";
115 char Eio[] = "i/o error";
116 char Eempty[] = "directory is not empty";
117 char Emode[] = "illegal mode";
119 int dflag;
121 void
122 notifyf(void *a, char *s)
124 USED(a);
125 if(strncmp(s, "interrupt", 9) == 0)
126 noted(NCONT);
127 noted(NDFLT);
130 void
131 threadmain(int argc, char *argv[])
133 char *defsrv, *q;
134 int p[2], l;
135 int stdio = 0;
136 char *host = nil;
137 long ncache = 1000;
138 int readOnly = 1;
140 defsrv = nil;
141 ARGBEGIN{
142 case 'd':
143 fmtinstall('F', fcallfmt);
144 dflag = 1;
145 break;
146 case 'c':
147 ncache = atoi(EARGF(usage()));
148 break;
149 case 'i':
150 stdio = 1;
151 mfd[0] = 0;
152 mfd[1] = 1;
153 break;
154 case 'h':
155 host = EARGF(usage());
156 break;
157 case 's':
158 defsrv = EARGF(usage());
159 break;
160 case 'p':
161 noperm = 1;
162 break;
163 default:
164 usage();
165 }ARGEND
167 if(argc != 1)
168 usage();
170 init(argv[0], host, ncache, readOnly);
172 if(pipe(p) < 0)
173 sysfatal("pipe failed: %r");
175 mfd[0] = p[0];
176 mfd[1] = p[0];
177 proccreate(srv, 0, 32 * 1024);
179 if(defsrv == nil){
180 q = strrchr(argv[0], '/');
181 if(q)
182 q++;
183 else
184 q = argv[0];
185 defsrv = vtmalloc(6+strlen(q)+1);
186 strcpy(defsrv, "vacfs.");
187 strcat(defsrv, q);
188 l = strlen(defsrv);
189 if(strcmp(defsrv+l-4, ".vac") == 0)
190 defsrv[l-4] = 0;
193 if(post9pservice(p[1], defsrv) != 0)
194 sysfatal("post9pservice");
197 threadexits(0);
200 void srv(void* a) {
201 io();
202 shutdown();
205 void
206 usage(void)
208 fprint(2, "usage: %s [-sd] [-h host] [-c ncache] [-m mountpoint] vacfile\n", argv0);
209 exits("usage");
212 char*
213 rversion(Fid *unused)
215 Fid *f;
217 USED(unused);
219 for(f = fids; f; f = f->next)
220 if(f->busy)
221 rclunk(f);
223 if(rhdr.msize < 256)
224 return vtstrdup("version: message size too small");
225 messagesize = rhdr.msize;
226 if(messagesize > sizeof mdata)
227 messagesize = sizeof mdata;
228 thdr.msize = messagesize;
229 if(strncmp(rhdr.version, "9P2000", 6) != 0)
230 return vtstrdup("unrecognized 9P version");
231 thdr.version = "9P2000";
232 return nil;
235 char*
236 rflush(Fid *f)
238 USED(f);
239 return 0;
242 char*
243 rauth(Fid *f)
245 USED(f);
246 return vtstrdup("vacfs: authentication not required");
249 char*
250 rattach(Fid *f)
252 /* no authentication for the momment */
253 VacFile *file;
254 char err[80];
256 file = vacfsgetroot(fs);
257 if(file == nil) {
258 rerrstr(err, sizeof err);
259 return vtstrdup(err);
262 f->busy = 1;
263 f->file = file;
264 f->qid = (Qid){vacfilegetid(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 = vacfilewalk(file, name);
279 if(n)
280 return n;
281 if(strcmp(name, "SLASH") == 0)
282 return vacfilewalk(file, "/");
283 return nil;
286 char*
287 rwalk(Fid *f)
289 VacFile *file, *nfile;
290 Fid *nf;
291 int nqid, nwname;
292 Qid qid;
293 char *err = nil;
295 if(f->busy == 0)
296 return Enotexist;
297 nf = nil;
298 if(rhdr.fid != rhdr.newfid){
299 if(f->open)
300 return vtstrdup(Eisopen);
301 if(f->busy == 0)
302 return vtstrdup(Enotexist);
303 nf = newfid(rhdr.newfid);
304 if(nf->busy)
305 return vtstrdup(Eisopen);
306 nf->busy = 1;
307 nf->open = 0;
308 nf->qid = f->qid;
309 nf->file = vacfileincref(f->file);
310 nf->user = vtstrdup(f->user);
311 f = nf;
314 nwname = rhdr.nwname;
316 /* easy case */
317 if(nwname == 0) {
318 thdr.nwqid = 0;
319 return 0;
322 file = f->file;
323 vacfileincref(file);
324 qid = f->qid;
326 for(nqid = 0; nqid < nwname; nqid++){
327 if((qid.type & QTDIR) == 0){
328 err = Enotdir;
329 break;
331 if(!permf(file, f->user, Pexec)) {
332 err = Eperm;
333 break;
335 nfile = _vfWalk(file, rhdr.wname[nqid]);
336 if(nfile == nil)
337 break;
338 vacfiledecref(file);
339 file = nfile;
340 qid.type = QTFILE;
341 if(vacfileisdir(file))
342 qid.type = QTDIR;
343 qid.vers = vacfilegetmcount(file);
344 qid.path = vacfilegetid(file);
345 thdr.wqid[nqid] = qid;
348 thdr.nwqid = nqid;
350 if(nqid == nwname){
351 /* success */
352 f->qid = thdr.wqid[nqid-1];
353 vacfiledecref(f->file);
354 f->file = file;
355 return 0;
358 vacfiledecref(file);
359 if(nf != nil)
360 rclunk(nf);
362 /* only error on the first element */
363 if(nqid == 0)
364 return vtstrdup(err);
366 return 0;
369 char *
370 ropen(Fid *f)
372 int mode, trunc;
374 if(f->open)
375 return vtstrdup(Eisopen);
376 if(!f->busy)
377 return vtstrdup(Enotexist);
379 mode = rhdr.mode;
380 thdr.iounit = messagesize - IOHDRSZ;
381 if(f->qid.type & QTDIR){
382 if(mode != OREAD)
383 return vtstrdup(Eperm);
384 if(!perm(f, Pread))
385 return vtstrdup(Eperm);
386 thdr.qid = f->qid;
387 f->db = nil;
388 f->open = 1;
389 return 0;
391 if(mode & ORCLOSE)
392 return vtstrdup(Erdonly);
393 trunc = mode & OTRUNC;
394 mode &= OPERM;
395 if(mode==OWRITE || mode==ORDWR || trunc)
396 if(!perm(f, Pwrite))
397 return vtstrdup(Eperm);
398 if(mode==OREAD || mode==ORDWR)
399 if(!perm(f, Pread))
400 return vtstrdup(Eperm);
401 if(mode==OEXEC)
402 if(!perm(f, Pexec))
403 return vtstrdup(Eperm);
404 thdr.qid = f->qid;
405 thdr.iounit = messagesize - IOHDRSZ;
406 f->open = 1;
407 return 0;
410 char*
411 rcreate(Fid* fid)
413 VacFile *vf;
414 ulong mode;
416 if(fid->open)
417 return vtstrdup(Eisopen);
418 if(!fid->busy)
419 return vtstrdup(Enotexist);
420 if(fs->mode & ModeSnapshot)
421 return vtstrdup(Erdonly);
422 vf = fid->file;
423 if(!vacfileisdir(vf))
424 return vtstrdup(Enotdir);
425 if(!permf(vf, fid->user, Pwrite))
426 return vtstrdup(Eperm);
428 mode = rhdr.perm & 0777;
430 if(rhdr.perm & DMDIR){
431 if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))
432 return vtstrdup(Emode);
433 switch(rhdr.mode & OPERM){
434 default:
435 return vtstrdup(Emode);
436 case OEXEC:
437 case OREAD:
438 break;
439 case OWRITE:
440 case ORDWR:
441 return vtstrdup(Eperm);
443 mode |= ModeDir;
445 vf = vacfilecreate(vf, rhdr.name, mode, "none");
446 if(vf == nil) {
447 char err[80];
448 rerrstr(err, sizeof err);
450 return vtstrdup(err);
453 vacfiledecref(fid->file);
455 fid->file = vf;
456 fid->qid.type = QTFILE;
457 if(vacfileisdir(vf))
458 fid->qid.type = QTDIR;
459 fid->qid.vers = vacfilegetmcount(vf);
460 fid->qid.path = vacfilegetid(vf);
462 thdr.qid = fid->qid;
463 thdr.iounit = messagesize - IOHDRSZ;
465 return 0;
468 char*
469 rread(Fid *f)
471 char *buf;
472 vlong off;
473 int cnt;
474 VacFile *vf;
475 char err[80];
476 int n;
478 if(!f->busy)
479 return vtstrdup(Enotexist);
480 vf = f->file;
481 thdr.count = 0;
482 off = rhdr.offset;
483 buf = thdr.data;
484 cnt = rhdr.count;
485 if(f->qid.type & QTDIR)
486 n = vacdirread(f, buf, off, cnt);
487 else
488 n = vacfileread(vf, buf, cnt, off);
489 if(n < 0) {
490 rerrstr(err, sizeof err);
491 return vtstrdup(err);
493 thdr.count = n;
494 return 0;
497 char*
498 rwrite(Fid *f)
500 char *buf;
501 vlong off;
502 int cnt;
503 VacFile *vf;
505 if(!f->busy)
506 return vtstrdup(Enotexist);
507 vf = f->file;
508 thdr.count = 0;
509 off = rhdr.offset;
510 buf = rhdr.data;
511 cnt = rhdr.count;
512 if(f->qid.type & QTDIR)
513 return "file is a directory";
514 thdr.count = vacfilewrite(vf, buf, cnt, off, "none");
515 if(thdr.count < 0) {
516 char err[80];
518 rerrstr(err, sizeof err);
519 fprint(2, "write failed: %s\n", err);
520 return vtstrdup(err);
522 return 0;
525 char *
526 rclunk(Fid *f)
528 f->busy = 0;
529 f->open = 0;
530 vtfree(f->user);
531 f->user = nil;
532 vacfiledecref(f->file);
533 f->file = nil;
534 dirBufFree(f->db);
535 f->db = nil;
536 return 0;
539 char *
540 rremove(Fid *f)
542 VacFile *vf, *vfp;
543 char errbuf[80];
544 char *err = nil;
546 if(!f->busy)
547 return vtstrdup(Enotexist);
548 vf = f->file;
549 vfp = vacfilegetparent(vf);
551 if(!permf(vfp, f->user, Pwrite)) {
552 err = Eperm;
553 goto Exit;
556 if(!vacfileremove(vf, "none")) {
557 rerrstr(errbuf, sizeof errbuf);
558 print("vfRemove failed: %s\n", errbuf);
560 err = errbuf;
563 Exit:
564 vacfiledecref(vfp);
565 rclunk(f);
566 return vtstrdup(err);
569 char *
570 rstat(Fid *f)
572 VacDir dir;
573 static uchar statbuf[1024];
575 if(!f->busy)
576 return vtstrdup(Enotexist);
577 vacfilegetdir(f->file, &dir);
578 thdr.stat = statbuf;
579 thdr.nstat = vdStat(&dir, thdr.stat, sizeof statbuf);
580 vdcleanup(&dir);
581 return 0;
584 char *
585 rwstat(Fid *f)
587 if(!f->busy)
588 return vtstrdup(Enotexist);
589 return vtstrdup(Erdonly);
592 int
593 vdStat(VacDir *vd, uchar *p, int np)
595 Dir dir;
597 memset(&dir, 0, sizeof(dir));
599 /*
600 * Where do path and version come from
601 */
602 dir.qid.path = vd->qid;
603 dir.qid.vers = vd->mcount;
604 dir.mode = vd->mode & 0777;
605 if(vd->mode & ModeAppend){
606 dir.qid.type |= QTAPPEND;
607 dir.mode |= DMAPPEND;
609 if(vd->mode & ModeExclusive){
610 dir.qid.type |= QTEXCL;
611 dir.mode |= DMEXCL;
613 if(vd->mode & ModeDir){
614 dir.qid.type |= QTDIR;
615 dir.mode |= DMDIR;
618 dir.atime = vd->atime;
619 dir.mtime = vd->mtime;
620 dir.length = vd->size;
622 dir.name = vd->elem;
623 dir.uid = vd->uid;
624 dir.gid = vd->gid;
625 dir.muid = vd->mid;
627 return convD2M(&dir, p, np);
630 DirBuf*
631 dirBufAlloc(VacFile *vf)
633 DirBuf *db;
635 db = vtmallocz(sizeof(DirBuf));
636 db->vde = vdeopen(vf);
637 return db;
640 VacDir *
641 dirBufGet(DirBuf *db)
643 VacDir *vd;
644 int n;
646 if(db->eof)
647 return nil;
649 if(db->i >= db->n) {
650 n = vderead(db->vde, db->buf);
651 if(n < 0)
652 return nil;
653 db->i = 0;
654 db->n = n;
655 if(n == 0) {
656 db->eof = 1;
657 return nil;
661 vd = db->buf + db->i;
662 db->i++;
664 return vd;
667 int
668 dirBufUnget(DirBuf *db)
670 assert(db->i > 0);
671 db->i--;
672 return 1;
675 void
676 dirBufFree(DirBuf *db)
678 int i;
680 if(db == nil)
681 return;
683 for(i=db->i; i<db->n; i++)
684 vdcleanup(db->buf + i);
685 vdeclose(db->vde);
686 vtfree(db);
689 int
690 vacdirread(Fid *f, char *p, long off, long cnt)
692 int n, nb;
693 VacDir *vd;
695 /*
696 * special case of rewinding a directory
697 * otherwise ignore the offset
698 */
699 if(off == 0 && f->db) {
700 dirBufFree(f->db);
701 f->db = nil;
704 if(f->db == nil)
705 f->db = dirBufAlloc(f->file);
707 for(nb = 0; nb < cnt; nb += n) {
708 vd = dirBufGet(f->db);
709 if(vd == nil) {
710 if(!f->db->eof)
711 return -1;
712 break;
714 n = vdStat(vd, (uchar*)p, cnt-nb);
715 if(n <= BIT16SZ) {
716 dirBufUnget(f->db);
717 break;
719 vdcleanup(vd);
720 p += n;
722 return nb;
725 Fid *
726 newfid(int fid)
728 Fid *f, *ff;
730 ff = 0;
731 for(f = fids; f; f = f->next)
732 if(f->fid == fid)
733 return f;
734 else if(!ff && !f->busy)
735 ff = f;
736 if(ff){
737 ff->fid = fid;
738 return ff;
740 f = vtmallocz(sizeof *f);
741 f->fid = fid;
742 f->next = fids;
743 fids = f;
744 return f;
747 void
748 io(void)
750 char *err;
751 int n;
753 for(;;){
754 /*
755 * reading from a pipe or a network device
756 * will give an error after a few eof reads
757 * however, we cannot tell the difference
758 * between a zero-length read and an interrupt
759 * on the processes writing to us,
760 * so we wait for the error
761 */
762 n = read9pmsg(mfd[0], mdata, sizeof mdata);
763 if(n == 0)
764 continue;
765 if(n < 0)
766 break;
767 if(convM2S(mdata, n, &rhdr) != n)
768 sysfatal("convM2S conversion error");
770 if(dflag)
771 fprint(2, "vacfs:<-%F\n", &rhdr);
773 thdr.data = (char*)mdata + IOHDRSZ;
774 if(!fcalls[rhdr.type])
775 err = "bad fcall type";
776 else
777 err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
778 if(err){
779 thdr.type = Rerror;
780 thdr.ename = err;
781 }else{
782 thdr.type = rhdr.type + 1;
783 thdr.fid = rhdr.fid;
785 thdr.tag = rhdr.tag;
786 if(dflag)
787 fprint(2, "vacfs:->%F\n", &thdr);
788 n = convS2M(&thdr, mdata, messagesize);
789 if (err)
790 vtfree(err);
792 if(write(mfd[1], mdata, n) != n)
793 sysfatal("mount write: %r");
797 int
798 permf(VacFile *vf, char *user, int p)
800 VacDir dir;
801 ulong perm;
803 if(vacfilegetdir(vf, &dir))
804 return 0;
805 perm = dir.mode & 0777;
807 if(noperm)
808 goto Good;
809 if((p*Pother) & perm)
810 goto Good;
811 if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm))
812 goto Good;
813 if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm))
814 goto Good;
815 vdcleanup(&dir);
816 return 0;
817 Good:
818 vdcleanup(&dir);
819 return 1;
822 int
823 perm(Fid *f, int p)
825 return permf(f->file, f->user, p);
828 void
829 init(char *file, char *host, long ncache, int readOnly)
831 notify(notifyf);
832 user = getuser();
834 fmtinstall('V', vtscorefmt);
835 // fmtinstall('R', vtErrFmt);
837 conn = vtdial(host);
838 if(conn == nil)
839 sysfatal("could not connect to server: %r");
841 if(vtconnect(conn) < 0)
842 sysfatal("vtconnect: %r");
844 fs = vacfsopen(conn, file, /*readOnly ? ModeSnapshot :*/ VtOREAD, ncache);
845 if(fs == nil)
846 sysfatal("vfsOpen: %r");
849 void
850 shutdown(void)
852 Fid *f;
854 for(f = fids; f; f = f->next) {
855 if(!f->busy)
856 continue;
857 fprint(2, "open fid: %d\n", f->fid);
858 rclunk(f);
861 vacfsclose(fs);
862 vthangup(conn);