Blob


1 #include "stdinc.h"
2 #include <fcall.h>
3 #include "vac.h"
5 typedef struct Fid Fid;
7 enum
8 {
9 OPERM = 0x3 /* mask of all permission types in open mode */
10 };
12 struct Fid
13 {
14 short busy;
15 short open;
16 int fid;
17 char *user;
18 Qid qid;
19 VacFile *file;
20 VacDirEnum *vde;
21 Fid *next;
22 };
24 enum
25 {
26 Pexec = 1,
27 Pwrite = 2,
28 Pread = 4,
29 Pother = 1,
30 Pgroup = 8,
31 Powner = 64
32 };
34 Fid *fids;
35 uchar *data;
36 int mfd[2];
37 int srvfd = -1;
38 char *user;
39 uchar mdata[8192+IOHDRSZ];
40 int messagesize = sizeof mdata;
41 Fcall rhdr;
42 Fcall thdr;
43 VacFs *fs;
44 VtConn *conn;
45 int noperm;
46 char *defmnt;
48 Fid * newfid(int);
49 void error(char*);
50 void io(void);
51 void vacshutdown(void);
52 void usage(void);
53 int perm(Fid*, int);
54 int permf(VacFile*, char*, int);
55 ulong getl(void *p);
56 void init(char*, char*, long, int);
57 int vacdirread(Fid *f, char *p, long off, long cnt);
58 int vacstat(VacFile *parent, VacDir *vd, uchar *p, int np);
59 void srv(void* a);
62 char *rflush(Fid*), *rversion(Fid*),
63 *rauth(Fid*), *rattach(Fid*), *rwalk(Fid*),
64 *ropen(Fid*), *rcreate(Fid*),
65 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
66 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
68 char *(*fcalls[Tmax])(Fid*);
70 void
71 initfcalls(void)
72 {
73 fcalls[Tflush]= rflush;
74 fcalls[Tversion]= rversion;
75 fcalls[Tattach]= rattach;
76 fcalls[Tauth]= rauth;
77 fcalls[Twalk]= rwalk;
78 fcalls[Topen]= ropen;
79 fcalls[Tcreate]= rcreate;
80 fcalls[Tread]= rread;
81 fcalls[Twrite]= rwrite;
82 fcalls[Tclunk]= rclunk;
83 fcalls[Tremove]= rremove;
84 fcalls[Tstat]= rstat;
85 fcalls[Twstat]= rwstat;
86 }
88 char Eperm[] = "permission denied";
89 char Enotdir[] = "not a directory";
90 char Enotexist[] = "file does not exist";
91 char Einuse[] = "file in use";
92 char Eexist[] = "file exists";
93 char Enotowner[] = "not owner";
94 char Eisopen[] = "file already open for I/O";
95 char Excl[] = "exclusive use file already open";
96 char Ename[] = "illegal name";
97 char Erdonly[] = "read only file system";
98 char Eio[] = "i/o error";
99 char Eempty[] = "directory is not empty";
100 char Emode[] = "illegal mode";
102 int dflag;
104 void
105 notifyf(void *a, char *s)
107 USED(a);
108 if(strncmp(s, "interrupt", 9) == 0)
109 noted(NCONT);
110 noted(NDFLT);
113 #define TWID64 ~(u64int)0
114 static u64int
115 unittoull(char *s)
117 char *es;
118 u64int n;
120 if(s == nil)
121 return TWID64;
122 n = strtoul(s, &es, 0);
123 if(*es == 'k' || *es == 'K'){
124 n *= 1024;
125 es++;
126 }else if(*es == 'm' || *es == 'M'){
127 n *= 1024*1024;
128 es++;
129 }else if(*es == 'g' || *es == 'G'){
130 n *= 1024*1024*1024;
131 es++;
133 if(*es != '\0')
134 return TWID64;
135 return n;
138 void
139 threadmain(int argc, char *argv[])
141 char *defsrv, *srvname;
142 int p[2], fd;
143 int stdio;
144 char *host = nil;
145 ulong mem;
147 mem = 16<<20;
148 stdio = 0;
149 fmtinstall('H', encodefmt);
150 fmtinstall('V', vtscorefmt);
151 fmtinstall('F', vtfcallfmt);
153 defmnt = nil;
154 defsrv = nil;
155 ARGBEGIN{
156 case 'd':
157 fmtinstall('F', fcallfmt);
158 dflag = 1;
159 break;
160 case 'i':
161 defmnt = nil;
162 stdio = 1;
163 mfd[0] = 0;
164 mfd[1] = 1;
165 break;
166 case 'h':
167 host = EARGF(usage());
168 break;
169 case 'S':
170 defsrv = EARGF(usage());
171 break;
172 case 's':
173 defsrv = "vacfs";
174 break;
175 case 'M':
176 mem = unittoull(EARGF(usage()));
177 break;
178 case 'm':
179 defmnt = EARGF(usage());
180 break;
181 case 'p':
182 noperm = 1;
183 break;
184 case 'V':
185 chattyventi = 1;
186 break;
187 default:
188 usage();
189 }ARGEND
191 if(argc != 1)
192 usage();
194 #ifdef PLAN9PORT
195 if(defsrv == nil && defmnt == nil && !stdio){
196 srvname = strchr(argv[0], '/');
197 if(srvname)
198 srvname++;
199 else
200 srvname = argv[0];
201 defsrv = vtmalloc(6+strlen(srvname)+1);
202 strcpy(defsrv, "vacfs.");
203 strcat(defsrv, srvname);
204 if(strcmp(defsrv+strlen(defsrv)-4, ".vac") == 0)
205 defsrv[strlen(defsrv)-4] = 0;
207 #else
208 if(defsrv == nil && defmnt == nil && !stdio)
209 defmnt = "/n/vac";
210 #endif
211 if(stdio && defmnt)
212 sysfatal("cannot use -m with -i");
214 initfcalls();
216 notify(notifyf);
217 user = getuser();
219 conn = vtdial(host);
220 if(conn == nil)
221 sysfatal("could not connect to server: %r");
223 if(vtconnect(conn) < 0)
224 sysfatal("vtconnect: %r");
226 fs = vacfsopen(conn, argv[0], VtOREAD, mem);
227 if(fs == nil)
228 sysfatal("vacfsopen: %r");
230 if(!stdio){
231 if(pipe(p) < 0)
232 sysfatal("pipe failed: %r");
233 mfd[0] = p[0];
234 mfd[1] = p[0];
235 srvfd = p[1];
236 #ifndef PLAN9PORT
237 if(defsrv){
238 srvname = smprint("/srv/%s", defsrv);
239 fd = create(srvname, OWRITE|ORCLOSE, 0666);
240 if(fd < 0)
241 sysfatal("create %s: %r", srvname);
242 if(fprint(fd, "%d", srvfd) < 0)
243 sysfatal("write %s: %r", srvname);
244 free(srvname);
246 #endif
249 #ifdef PLAN9PORT
250 USED(fd);
251 proccreate(srv, 0, 32 * 1024);
252 if(!stdio && post9pservice(p[1], defsrv, defmnt) < 0)
253 sysfatal("post9pservice");
254 #else
255 procrfork(srv, 0, 32 * 1024, RFFDG|RFNAMEG|RFNOTEG);
257 if(!stdio){
258 close(p[0]);
259 if(defmnt){
260 if(mount(srvfd, -1, defmnt, MREPL|MCREATE, "") < 0)
261 sysfatal("mount %s: %r", defmnt);
264 #endif
265 threadexits(0);
268 void
269 srv(void *a)
271 USED(a);
272 io();
273 vacshutdown();
276 void
277 usage(void)
279 fprint(2, "usage: %s [-sd] [-h host] [-m mountpoint] [-M mem] vacfile\n", argv0);
280 threadexitsall("usage");
283 char*
284 rversion(Fid *unused)
286 Fid *f;
288 USED(unused);
290 for(f = fids; f; f = f->next)
291 if(f->busy)
292 rclunk(f);
294 if(rhdr.msize < 256)
295 return vtstrdup("version: message size too small");
296 messagesize = rhdr.msize;
297 if(messagesize > sizeof mdata)
298 messagesize = sizeof mdata;
299 thdr.msize = messagesize;
300 if(strncmp(rhdr.version, "9P2000", 6) != 0)
301 return vtstrdup("unrecognized 9P version");
302 thdr.version = "9P2000";
303 return nil;
306 char*
307 rflush(Fid *f)
309 USED(f);
310 return 0;
313 char*
314 rauth(Fid *f)
316 USED(f);
317 return vtstrdup("vacfs: authentication not required");
320 char*
321 rattach(Fid *f)
323 /* no authentication for the momment */
324 VacFile *file;
325 char err[80];
327 file = vacfsgetroot(fs);
328 if(file == nil) {
329 rerrstr(err, sizeof err);
330 return vtstrdup(err);
333 f->busy = 1;
334 f->file = file;
335 f->qid.path = vacfilegetid(f->file);
336 f->qid.vers = 0;
337 f->qid.type = QTDIR;
338 thdr.qid = f->qid;
339 if(rhdr.uname[0])
340 f->user = vtstrdup(rhdr.uname);
341 else
342 f->user = "none";
343 return 0;
346 char*
347 rwalk(Fid *f)
349 VacFile *file, *nfile;
350 Fid *nf;
351 int nqid, nwname;
352 Qid qid;
353 char *err = nil;
355 if(f->busy == 0)
356 return Enotexist;
357 nf = nil;
358 if(rhdr.fid != rhdr.newfid){
359 if(f->open)
360 return vtstrdup(Eisopen);
361 if(f->busy == 0)
362 return vtstrdup(Enotexist);
363 nf = newfid(rhdr.newfid);
364 if(nf->busy)
365 return vtstrdup(Eisopen);
366 nf->busy = 1;
367 nf->open = 0;
368 nf->qid = f->qid;
369 nf->file = vacfileincref(f->file);
370 nf->user = vtstrdup(f->user);
371 f = nf;
374 nwname = rhdr.nwname;
376 /* easy case */
377 if(nwname == 0) {
378 thdr.nwqid = 0;
379 return 0;
382 file = f->file;
383 vacfileincref(file);
384 qid = f->qid;
386 for(nqid = 0; nqid < nwname; nqid++){
387 if((qid.type & QTDIR) == 0){
388 err = Enotdir;
389 break;
391 if(!permf(file, f->user, Pexec)) {
392 err = Eperm;
393 break;
395 nfile = vacfilewalk(file, rhdr.wname[nqid]);
396 if(nfile == nil)
397 break;
398 vacfiledecref(file);
399 file = nfile;
400 qid.type = QTFILE;
401 if(vacfileisdir(file))
402 qid.type = QTDIR;
403 #ifdef PLAN9PORT
404 if(vacfilegetmode(file)&ModeLink)
405 qid.type = QTSYMLINK;
406 #endif
407 qid.vers = vacfilegetmcount(file);
408 qid.path = vacfilegetid(file);
409 thdr.wqid[nqid] = qid;
412 thdr.nwqid = nqid;
414 if(nqid == nwname){
415 /* success */
416 f->qid = thdr.wqid[nqid-1];
417 vacfiledecref(f->file);
418 f->file = file;
419 return 0;
422 vacfiledecref(file);
423 if(nf != nil)
424 rclunk(nf);
426 /* only error on the first element */
427 if(nqid == 0)
428 return vtstrdup(err);
430 return 0;
433 char *
434 ropen(Fid *f)
436 int mode, trunc;
438 if(f->open)
439 return vtstrdup(Eisopen);
440 if(!f->busy)
441 return vtstrdup(Enotexist);
443 mode = rhdr.mode;
444 thdr.iounit = messagesize - IOHDRSZ;
445 if(f->qid.type & QTDIR){
446 if(mode != OREAD)
447 return vtstrdup(Eperm);
448 if(!perm(f, Pread))
449 return vtstrdup(Eperm);
450 thdr.qid = f->qid;
451 f->vde = nil;
452 f->open = 1;
453 return 0;
455 if(mode & ORCLOSE)
456 return vtstrdup(Erdonly);
457 trunc = mode & OTRUNC;
458 mode &= OPERM;
459 if(mode==OWRITE || mode==ORDWR || trunc)
460 if(!perm(f, Pwrite))
461 return vtstrdup(Eperm);
462 if(mode==OREAD || mode==ORDWR)
463 if(!perm(f, Pread))
464 return vtstrdup(Eperm);
465 if(mode==OEXEC)
466 if(!perm(f, Pexec))
467 return vtstrdup(Eperm);
468 thdr.qid = f->qid;
469 thdr.iounit = messagesize - IOHDRSZ;
470 f->open = 1;
471 return 0;
474 char*
475 rcreate(Fid* fid)
477 VacFile *vf;
478 ulong mode;
480 if(fid->open)
481 return vtstrdup(Eisopen);
482 if(!fid->busy)
483 return vtstrdup(Enotexist);
484 if(fs->mode & ModeSnapshot)
485 return vtstrdup(Erdonly);
486 vf = fid->file;
487 if(!vacfileisdir(vf))
488 return vtstrdup(Enotdir);
489 if(!permf(vf, fid->user, Pwrite))
490 return vtstrdup(Eperm);
492 mode = rhdr.perm & 0777;
494 if(rhdr.perm & DMDIR){
495 if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))
496 return vtstrdup(Emode);
497 switch(rhdr.mode & OPERM){
498 default:
499 return vtstrdup(Emode);
500 case OEXEC:
501 case OREAD:
502 break;
503 case OWRITE:
504 case ORDWR:
505 return vtstrdup(Eperm);
507 mode |= ModeDir;
509 vf = vacfilecreate(vf, rhdr.name, mode);
510 if(vf == nil) {
511 char err[80];
512 rerrstr(err, sizeof err);
514 return vtstrdup(err);
517 vacfiledecref(fid->file);
519 fid->file = vf;
520 fid->qid.type = QTFILE;
521 if(vacfileisdir(vf))
522 fid->qid.type = QTDIR;
523 fid->qid.vers = vacfilegetmcount(vf);
524 fid->qid.path = vacfilegetid(vf);
526 thdr.qid = fid->qid;
527 thdr.iounit = messagesize - IOHDRSZ;
529 return 0;
532 char*
533 rread(Fid *f)
535 char *buf;
536 vlong off;
537 int cnt;
538 VacFile *vf;
539 char err[80];
540 int n;
542 if(!f->busy)
543 return vtstrdup(Enotexist);
544 vf = f->file;
545 thdr.count = 0;
546 off = rhdr.offset;
547 buf = thdr.data;
548 cnt = rhdr.count;
549 if(f->qid.type & QTDIR)
550 n = vacdirread(f, buf, off, cnt);
551 else if(vacfilegetmode(f->file)&ModeDevice)
552 return vtstrdup("device");
553 else if(vacfilegetmode(f->file)&ModeLink)
554 return vtstrdup("symbolic link");
555 else if(vacfilegetmode(f->file)&ModeNamedPipe)
556 return vtstrdup("named pipe");
557 else
558 n = vacfileread(vf, buf, cnt, off);
559 if(n < 0) {
560 rerrstr(err, sizeof err);
561 return vtstrdup(err);
563 thdr.count = n;
564 return 0;
567 char*
568 rwrite(Fid *f)
570 USED(f);
571 return vtstrdup(Erdonly);
574 char *
575 rclunk(Fid *f)
577 f->busy = 0;
578 f->open = 0;
579 vtfree(f->user);
580 f->user = nil;
581 if(f->file)
582 vacfiledecref(f->file);
583 f->file = nil;
584 vdeclose(f->vde);
585 f->vde = nil;
586 return 0;
589 char *
590 rremove(Fid *f)
592 VacFile *vf, *vfp;
593 char errbuf[80];
594 char *err = nil;
596 if(!f->busy)
597 return vtstrdup(Enotexist);
598 vf = f->file;
599 vfp = vacfilegetparent(vf);
601 if(!permf(vfp, f->user, Pwrite)) {
602 err = Eperm;
603 goto Exit;
606 if(!vacfileremove(vf)) {
607 rerrstr(errbuf, sizeof errbuf);
608 err = errbuf;
611 Exit:
612 vacfiledecref(vfp);
613 rclunk(f);
614 return vtstrdup(err);
617 char *
618 rstat(Fid *f)
620 VacDir dir;
621 static uchar statbuf[1024];
622 VacFile *parent;
624 if(!f->busy)
625 return vtstrdup(Enotexist);
626 parent = vacfilegetparent(f->file);
627 vacfilegetdir(f->file, &dir);
628 thdr.stat = statbuf;
629 thdr.nstat = vacstat(parent, &dir, thdr.stat, sizeof statbuf);
630 vdcleanup(&dir);
631 vacfiledecref(parent);
632 return 0;
635 char *
636 rwstat(Fid *f)
638 if(!f->busy)
639 return vtstrdup(Enotexist);
640 return vtstrdup(Erdonly);
643 int
644 vacstat(VacFile *parent, VacDir *vd, uchar *p, int np)
646 int ret;
647 Dir dir;
648 #ifdef PLAN9PORT
649 int n;
650 VacFile *vf;
651 uvlong size;
652 char *ext = nil;
653 #endif
655 memset(&dir, 0, sizeof(dir));
657 dir.qid.path = vd->qid + vacfilegetqidoffset(parent);
658 if(vd->qidspace)
659 dir.qid.path += vd->qidoffset;
660 dir.qid.vers = vd->mcount;
661 dir.mode = vd->mode & 0777;
662 if(vd->mode & ModeAppend){
663 dir.qid.type |= QTAPPEND;
664 dir.mode |= DMAPPEND;
666 if(vd->mode & ModeExclusive){
667 dir.qid.type |= QTEXCL;
668 dir.mode |= DMEXCL;
670 if(vd->mode & ModeDir){
671 dir.qid.type |= QTDIR;
672 dir.mode |= DMDIR;
675 #ifdef PLAN9PORT
676 if(vd->mode & (ModeLink|ModeDevice|ModeNamedPipe)){
677 vf = vacfilewalk(parent, vd->elem);
678 if(vf == nil)
679 return 0;
680 vacfilegetsize(vf, &size);
681 ext = malloc(size+1);
682 if(ext == nil)
683 return 0;
684 n = vacfileread(vf, ext, size, 0);
685 USED(n);
686 ext[size] = 0;
687 vacfiledecref(vf);
688 if(vd->mode & ModeLink){
689 dir.qid.type |= QTSYMLINK;
690 dir.mode |= DMSYMLINK;
692 if(vd->mode & ModeDevice)
693 dir.mode |= DMDEVICE;
694 if(vd->mode & ModeNamedPipe)
695 dir.mode |= DMNAMEDPIPE;
697 #endif
699 dir.atime = vd->atime;
700 dir.mtime = vd->mtime;
701 dir.length = vd->size;
703 dir.name = vd->elem;
704 dir.uid = vd->uid;
705 dir.gid = vd->gid;
706 dir.muid = vd->mid;
708 ret = convD2M(&dir, p, np);
709 #ifdef PLAN9PORT
710 free(ext);
711 #endif
712 return ret;
715 int
716 vacdirread(Fid *f, char *p, long off, long cnt)
718 int i, n, nb;
719 VacDir vd;
721 /*
722 * special case of rewinding a directory
723 * otherwise ignore the offset
724 */
725 if(off == 0 && f->vde){
726 vdeclose(f->vde);
727 f->vde = nil;
730 if(f->vde == nil){
731 f->vde = vdeopen(f->file);
732 if(f->vde == nil)
733 return -1;
736 for(nb = 0; nb < cnt; nb += n) {
737 i = vderead(f->vde, &vd);
738 if(i < 0)
739 return -1;
740 if(i == 0)
741 break;
742 n = vacstat(f->file, &vd, (uchar*)p, cnt-nb);
743 if(n <= BIT16SZ) {
744 vdeunread(f->vde);
745 break;
747 vdcleanup(&vd);
748 p += n;
750 return nb;
753 Fid *
754 newfid(int fid)
756 Fid *f, *ff;
758 ff = 0;
759 for(f = fids; f; f = f->next)
760 if(f->fid == fid)
761 return f;
762 else if(!ff && !f->busy)
763 ff = f;
764 if(ff){
765 ff->fid = fid;
766 return ff;
768 f = vtmallocz(sizeof *f);
769 f->fid = fid;
770 f->next = fids;
771 fids = f;
772 return f;
775 void
776 io(void)
778 char *err;
779 int n;
781 for(;;){
782 n = read9pmsg(mfd[0], mdata, sizeof mdata);
783 if(n <= 0)
784 break;
785 if(convM2S(mdata, n, &rhdr) != n)
786 sysfatal("convM2S conversion error");
788 if(dflag)
789 fprint(2, "vacfs:<-%F\n", &rhdr);
791 thdr.data = (char*)mdata + IOHDRSZ;
792 if(!fcalls[rhdr.type])
793 err = "bad fcall type";
794 else
795 err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
796 if(err){
797 thdr.type = Rerror;
798 thdr.ename = err;
799 #ifdef PLAN9PORT
800 thdr.errornum = 0;
801 #endif
802 }else{
803 thdr.type = rhdr.type + 1;
804 thdr.fid = rhdr.fid;
806 thdr.tag = rhdr.tag;
807 if(dflag)
808 fprint(2, "vacfs:->%F\n", &thdr);
809 n = convS2M(&thdr, mdata, messagesize);
810 if(n <= BIT16SZ)
811 sysfatal("convS2M conversion error");
812 if(err)
813 vtfree(err);
815 if(write(mfd[1], mdata, n) != n)
816 sysfatal("mount write: %r");
820 int
821 permf(VacFile *vf, char *user, int p)
823 VacDir dir;
824 ulong perm;
826 if(vacfilegetdir(vf, &dir))
827 return 0;
828 perm = dir.mode & 0777;
830 if(noperm)
831 goto Good;
832 if((p*Pother) & perm)
833 goto Good;
834 if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm))
835 goto Good;
836 if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm))
837 goto Good;
838 vdcleanup(&dir);
839 return 0;
840 Good:
841 vdcleanup(&dir);
842 return 1;
845 int
846 perm(Fid *f, int p)
848 return permf(f->file, f->user, p);
851 void
852 vacshutdown(void)
854 Fid *f;
856 for(f = fids; f; f = f->next) {
857 if(!f->busy)
858 continue;
859 rclunk(f);
862 vacfsclose(fs);
863 vthangup(conn);