Blob


1 #include "stdinc.h"
2 #include <fcall.h>
3 #include "vac.h"
5 #ifndef PLAN9PORT
6 #define convM2Su(a, b, c, d) convM2S(a, b, c)
7 #define convS2Mu(a, b, c, d) convS2M(a, b, c)
8 #define convM2Du(a, b, c, d) convM2D(a, b, c)
9 #define convD2Mu(a, b, c, d) convD2M(a, b, c)
10 #endif
12 typedef struct Fid Fid;
14 enum
15 {
16 OPERM = 0x3 /* mask of all permission types in open mode */
17 };
19 struct Fid
20 {
21 short busy;
22 short open;
23 int fid;
24 char *user;
25 Qid qid;
26 VacFile *file;
27 VacDirEnum *vde;
28 Fid *next;
29 };
31 enum
32 {
33 Pexec = 1,
34 Pwrite = 2,
35 Pread = 4,
36 Pother = 1,
37 Pgroup = 8,
38 Powner = 64
39 };
41 Fid *fids;
42 uchar *data;
43 int mfd[2];
44 int srvfd = -1;
45 char *user;
46 uchar mdata[8192+IOHDRSZ];
47 int messagesize = sizeof mdata;
48 Fcall rhdr;
49 Fcall thdr;
50 VacFs *fs;
51 VtConn *conn;
52 int noperm;
53 int dotu;
54 char *defmnt;
56 Fid * newfid(int);
57 void error(char*);
58 void io(void);
59 void vacshutdown(void);
60 void usage(void);
61 int perm(Fid*, int);
62 int permf(VacFile*, char*, int);
63 ulong getl(void *p);
64 void init(char*, char*, long, int);
65 int vacdirread(Fid *f, char *p, long off, long cnt);
66 int vacstat(VacFile *parent, VacDir *vd, uchar *p, int np);
67 void srv(void* a);
70 char *rflush(Fid*), *rversion(Fid*),
71 *rauth(Fid*), *rattach(Fid*), *rwalk(Fid*),
72 *ropen(Fid*), *rcreate(Fid*),
73 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
74 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
76 char *(*fcalls[Tmax])(Fid*);
78 void
79 initfcalls(void)
80 {
81 fcalls[Tflush]= rflush;
82 fcalls[Tversion]= rversion;
83 fcalls[Tattach]= rattach;
84 fcalls[Tauth]= rauth;
85 fcalls[Twalk]= rwalk;
86 fcalls[Topen]= ropen;
87 fcalls[Tcreate]= rcreate;
88 fcalls[Tread]= rread;
89 fcalls[Twrite]= rwrite;
90 fcalls[Tclunk]= rclunk;
91 fcalls[Tremove]= rremove;
92 fcalls[Tstat]= rstat;
93 fcalls[Twstat]= rwstat;
94 }
96 char Eperm[] = "permission denied";
97 char Enotdir[] = "not a directory";
98 char Enotexist[] = "file does not exist";
99 char Einuse[] = "file in use";
100 char Eexist[] = "file exists";
101 char Enotowner[] = "not owner";
102 char Eisopen[] = "file already open for I/O";
103 char Excl[] = "exclusive use file already open";
104 char Ename[] = "illegal name";
105 char Erdonly[] = "read only file system";
106 char Eio[] = "i/o error";
107 char Eempty[] = "directory is not empty";
108 char Emode[] = "illegal mode";
110 int dflag;
112 void
113 notifyf(void *a, char *s)
115 USED(a);
116 if(strncmp(s, "interrupt", 9) == 0)
117 noted(NCONT);
118 noted(NDFLT);
121 #define TWID64 ~(u64int)0
122 static u64int
123 unittoull(char *s)
125 char *es;
126 u64int n;
128 if(s == nil)
129 return TWID64;
130 n = strtoul(s, &es, 0);
131 if(*es == 'k' || *es == 'K'){
132 n *= 1024;
133 es++;
134 }else if(*es == 'm' || *es == 'M'){
135 n *= 1024*1024;
136 es++;
137 }else if(*es == 'g' || *es == 'G'){
138 n *= 1024*1024*1024;
139 es++;
141 if(*es != '\0')
142 return TWID64;
143 return n;
146 void
147 threadmain(int argc, char *argv[])
149 char *defsrv, *srvname;
150 int p[2], fd;
151 int stdio;
152 char *host = nil;
153 ulong mem;
155 mem = 16<<20;
156 stdio = 0;
157 fmtinstall('H', encodefmt);
158 fmtinstall('V', vtscorefmt);
159 fmtinstall('F', vtfcallfmt);
161 defmnt = nil;
162 defsrv = nil;
163 ARGBEGIN{
164 case 'd':
165 fmtinstall('F', fcallfmt);
166 dflag = 1;
167 break;
168 case 'i':
169 defmnt = nil;
170 stdio = 1;
171 mfd[0] = 0;
172 mfd[1] = 1;
173 break;
174 case 'h':
175 host = EARGF(usage());
176 break;
177 case 'S':
178 defsrv = EARGF(usage());
179 break;
180 case 's':
181 defsrv = "vacfs";
182 break;
183 case 'M':
184 mem = unittoull(EARGF(usage()));
185 break;
186 case 'm':
187 defmnt = EARGF(usage());
188 break;
189 case 'p':
190 noperm = 1;
191 break;
192 case 'V':
193 chattyventi = 1;
194 break;
195 default:
196 usage();
197 }ARGEND
199 if(argc != 1)
200 usage();
202 #ifdef PLAN9PORT
203 if(defsrv == nil && defmnt == nil && !stdio){
204 srvname = strchr(argv[0], '/');
205 if(srvname)
206 srvname++;
207 else
208 srvname = argv[0];
209 defsrv = vtmalloc(6+strlen(srvname)+1);
210 strcpy(defsrv, "vacfs.");
211 strcat(defsrv, srvname);
212 if(strcmp(defsrv+strlen(defsrv)-4, ".vac") == 0)
213 defsrv[strlen(defsrv)-4] = 0;
215 #else
216 if(defsrv == nil && defmnt == nil && !stdio)
217 defmnt = "/n/vac";
218 #endif
219 if(stdio && defmnt)
220 sysfatal("cannot use -m with -i");
222 initfcalls();
224 notify(notifyf);
225 user = getuser();
227 conn = vtdial(host);
228 if(conn == nil)
229 sysfatal("could not connect to server: %r");
231 if(vtconnect(conn) < 0)
232 sysfatal("vtconnect: %r");
234 fs = vacfsopen(conn, argv[0], VtOREAD, mem);
235 if(fs == nil)
236 sysfatal("vacfsopen: %r");
238 if(!stdio){
239 if(pipe(p) < 0)
240 sysfatal("pipe failed: %r");
241 mfd[0] = p[0];
242 mfd[1] = p[0];
243 srvfd = p[1];
246 #ifdef PLAN9PORT
247 USED(fd);
248 proccreate(srv, 0, 32 * 1024);
249 if(!stdio && post9pservice(p[1], defsrv, defmnt) < 0)
250 sysfatal("post9pservice");
251 #else
252 procrfork(srv, 0, 32 * 1024, RFFDG|RFNAMEG|RFNOTEG);
254 if(!stdio){
255 close(p[0]);
256 if(defsrv){
257 srvname = smprint("/srv/%s", defsrv);
258 fd = create(srvname, OWRITE|ORCLOSE, 0666);
259 if(fd < 0)
260 sysfatal("create %s: %r", srvname);
261 if(fprint(fd, "%d", srvfd) < 0)
262 sysfatal("write %s: %r", srvname);
263 free(srvname);
265 if(defmnt){
266 if(mount(srvfd, -1, defmnt, MREPL|MCREATE, "") < 0)
267 sysfatal("mount %s: %r", defmnt);
270 #endif
271 threadexits(0);
274 void
275 srv(void *a)
277 USED(a);
278 io();
279 vacshutdown();
282 void
283 usage(void)
285 fprint(2, "usage: %s [-sd] [-h host] [-c ncache] [-m mountpoint] vacfile\n", argv0);
286 threadexitsall("usage");
289 char*
290 rversion(Fid *unused)
292 Fid *f;
294 USED(unused);
296 for(f = fids; f; f = f->next)
297 if(f->busy)
298 rclunk(f);
300 if(rhdr.msize < 256)
301 return vtstrdup("version: message size too small");
302 messagesize = rhdr.msize;
303 if(messagesize > sizeof mdata)
304 messagesize = sizeof mdata;
305 thdr.msize = messagesize;
306 if(strncmp(rhdr.version, "9P2000", 6) != 0)
307 return vtstrdup("unrecognized 9P version");
308 thdr.version = "9P2000";
309 if(strncmp(rhdr.version, "9P2000.u", 8) == 0){
310 dotu = 1;
311 thdr.version = "9P2000.u";
313 return nil;
316 char*
317 rflush(Fid *f)
319 USED(f);
320 return 0;
323 char*
324 rauth(Fid *f)
326 USED(f);
327 return vtstrdup("vacfs: authentication not required");
330 char*
331 rattach(Fid *f)
333 /* no authentication for the momment */
334 VacFile *file;
335 char err[80];
337 file = vacfsgetroot(fs);
338 if(file == nil) {
339 rerrstr(err, sizeof err);
340 return vtstrdup(err);
343 f->busy = 1;
344 f->file = file;
345 f->qid.path = vacfilegetid(f->file);
346 f->qid.vers = 0;
347 f->qid.type = QTDIR;
348 thdr.qid = f->qid;
349 if(rhdr.uname[0])
350 f->user = vtstrdup(rhdr.uname);
351 else
352 f->user = "none";
353 return 0;
356 char*
357 rwalk(Fid *f)
359 VacFile *file, *nfile;
360 Fid *nf;
361 int nqid, nwname;
362 Qid qid;
363 char *err = nil;
365 if(f->busy == 0)
366 return Enotexist;
367 nf = nil;
368 if(rhdr.fid != rhdr.newfid){
369 if(f->open)
370 return vtstrdup(Eisopen);
371 if(f->busy == 0)
372 return vtstrdup(Enotexist);
373 nf = newfid(rhdr.newfid);
374 if(nf->busy)
375 return vtstrdup(Eisopen);
376 nf->busy = 1;
377 nf->open = 0;
378 nf->qid = f->qid;
379 nf->file = vacfileincref(f->file);
380 nf->user = vtstrdup(f->user);
381 f = nf;
384 nwname = rhdr.nwname;
386 /* easy case */
387 if(nwname == 0) {
388 thdr.nwqid = 0;
389 return 0;
392 file = f->file;
393 vacfileincref(file);
394 qid = f->qid;
396 for(nqid = 0; nqid < nwname; nqid++){
397 if((qid.type & QTDIR) == 0){
398 err = Enotdir;
399 break;
401 if(!permf(file, f->user, Pexec)) {
402 err = Eperm;
403 break;
405 nfile = vacfilewalk(file, rhdr.wname[nqid]);
406 if(nfile == nil)
407 break;
408 vacfiledecref(file);
409 file = nfile;
410 qid.type = QTFILE;
411 if(vacfileisdir(file))
412 qid.type = QTDIR;
413 #ifdef PLAN9PORT
414 if(vacfilegetmode(file)&ModeLink)
415 qid.type = QTSYMLINK;
416 #endif
417 qid.vers = vacfilegetmcount(file);
418 qid.path = vacfilegetid(file);
419 thdr.wqid[nqid] = qid;
422 thdr.nwqid = nqid;
424 if(nqid == nwname){
425 /* success */
426 f->qid = thdr.wqid[nqid-1];
427 vacfiledecref(f->file);
428 f->file = file;
429 return 0;
432 vacfiledecref(file);
433 if(nf != nil)
434 rclunk(nf);
436 /* only error on the first element */
437 if(nqid == 0)
438 return vtstrdup(err);
440 return 0;
443 char *
444 ropen(Fid *f)
446 int mode, trunc;
448 if(f->open)
449 return vtstrdup(Eisopen);
450 if(!f->busy)
451 return vtstrdup(Enotexist);
453 mode = rhdr.mode;
454 thdr.iounit = messagesize - IOHDRSZ;
455 if(f->qid.type & QTDIR){
456 if(mode != OREAD)
457 return vtstrdup(Eperm);
458 if(!perm(f, Pread))
459 return vtstrdup(Eperm);
460 thdr.qid = f->qid;
461 f->vde = nil;
462 f->open = 1;
463 return 0;
465 if(mode & ORCLOSE)
466 return vtstrdup(Erdonly);
467 trunc = mode & OTRUNC;
468 mode &= OPERM;
469 if(mode==OWRITE || mode==ORDWR || trunc)
470 if(!perm(f, Pwrite))
471 return vtstrdup(Eperm);
472 if(mode==OREAD || mode==ORDWR)
473 if(!perm(f, Pread))
474 return vtstrdup(Eperm);
475 if(mode==OEXEC)
476 if(!perm(f, Pexec))
477 return vtstrdup(Eperm);
478 thdr.qid = f->qid;
479 thdr.iounit = messagesize - IOHDRSZ;
480 f->open = 1;
481 return 0;
484 char*
485 rcreate(Fid* fid)
487 VacFile *vf;
488 ulong mode;
490 if(fid->open)
491 return vtstrdup(Eisopen);
492 if(!fid->busy)
493 return vtstrdup(Enotexist);
494 if(fs->mode & ModeSnapshot)
495 return vtstrdup(Erdonly);
496 vf = fid->file;
497 if(!vacfileisdir(vf))
498 return vtstrdup(Enotdir);
499 if(!permf(vf, fid->user, Pwrite))
500 return vtstrdup(Eperm);
502 mode = rhdr.perm & 0777;
504 if(rhdr.perm & DMDIR){
505 if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))
506 return vtstrdup(Emode);
507 switch(rhdr.mode & OPERM){
508 default:
509 return vtstrdup(Emode);
510 case OEXEC:
511 case OREAD:
512 break;
513 case OWRITE:
514 case ORDWR:
515 return vtstrdup(Eperm);
517 mode |= ModeDir;
519 vf = vacfilecreate(vf, rhdr.name, mode);
520 if(vf == nil) {
521 char err[80];
522 rerrstr(err, sizeof err);
524 return vtstrdup(err);
527 vacfiledecref(fid->file);
529 fid->file = vf;
530 fid->qid.type = QTFILE;
531 if(vacfileisdir(vf))
532 fid->qid.type = QTDIR;
533 fid->qid.vers = vacfilegetmcount(vf);
534 fid->qid.path = vacfilegetid(vf);
536 thdr.qid = fid->qid;
537 thdr.iounit = messagesize - IOHDRSZ;
539 return 0;
542 char*
543 rread(Fid *f)
545 char *buf;
546 vlong off;
547 int cnt;
548 VacFile *vf;
549 char err[80];
550 int n;
552 if(!f->busy)
553 return vtstrdup(Enotexist);
554 vf = f->file;
555 thdr.count = 0;
556 off = rhdr.offset;
557 buf = thdr.data;
558 cnt = rhdr.count;
559 if(f->qid.type & QTDIR)
560 n = vacdirread(f, buf, off, cnt);
561 else if(vacfilegetmode(f->file)&ModeDevice)
562 return vtstrdup("device");
563 else if(vacfilegetmode(f->file)&ModeLink)
564 return vtstrdup("symbolic link");
565 else if(vacfilegetmode(f->file)&ModeNamedPipe)
566 return vtstrdup("named pipe");
567 else
568 n = vacfileread(vf, buf, cnt, off);
569 if(n < 0) {
570 rerrstr(err, sizeof err);
571 return vtstrdup(err);
573 thdr.count = n;
574 return 0;
577 char*
578 rwrite(Fid *f)
580 USED(f);
581 return vtstrdup(Erdonly);
584 char *
585 rclunk(Fid *f)
587 f->busy = 0;
588 f->open = 0;
589 vtfree(f->user);
590 f->user = nil;
591 if(f->file)
592 vacfiledecref(f->file);
593 f->file = nil;
594 vdeclose(f->vde);
595 f->vde = nil;
596 return 0;
599 char *
600 rremove(Fid *f)
602 VacFile *vf, *vfp;
603 char errbuf[80];
604 char *err = nil;
606 if(!f->busy)
607 return vtstrdup(Enotexist);
608 vf = f->file;
609 vfp = vacfilegetparent(vf);
611 if(!permf(vfp, f->user, Pwrite)) {
612 err = Eperm;
613 goto Exit;
616 if(!vacfileremove(vf)) {
617 rerrstr(errbuf, sizeof errbuf);
618 err = errbuf;
621 Exit:
622 vacfiledecref(vfp);
623 rclunk(f);
624 return vtstrdup(err);
627 char *
628 rstat(Fid *f)
630 VacDir dir;
631 static uchar statbuf[1024];
632 VacFile *parent;
634 if(!f->busy)
635 return vtstrdup(Enotexist);
636 parent = vacfilegetparent(f->file);
637 vacfilegetdir(f->file, &dir);
638 thdr.stat = statbuf;
639 thdr.nstat = vacstat(parent, &dir, thdr.stat, sizeof statbuf);
640 vdcleanup(&dir);
641 vacfiledecref(parent);
642 return 0;
645 char *
646 rwstat(Fid *f)
648 if(!f->busy)
649 return vtstrdup(Enotexist);
650 return vtstrdup(Erdonly);
653 int
654 vacstat(VacFile *parent, VacDir *vd, uchar *p, int np)
656 int ret;
657 Dir dir;
658 #ifdef PLAN9PORT
659 int n;
660 VacFile *vf;
661 uvlong size;
662 char *ext = nil;
663 #endif
665 memset(&dir, 0, sizeof(dir));
667 dir.qid.path = vd->qid + vacfilegetqidoffset(parent);
668 if(vd->qidspace)
669 dir.qid.path += vd->qidoffset;
670 dir.qid.vers = vd->mcount;
671 dir.mode = vd->mode & 0777;
672 if(vd->mode & ModeAppend){
673 dir.qid.type |= QTAPPEND;
674 dir.mode |= DMAPPEND;
676 if(vd->mode & ModeExclusive){
677 dir.qid.type |= QTEXCL;
678 dir.mode |= DMEXCL;
680 if(vd->mode & ModeDir){
681 dir.qid.type |= QTDIR;
682 dir.mode |= DMDIR;
685 #ifdef PLAN9PORT
686 if(vd->mode & (ModeLink|ModeDevice|ModeNamedPipe)){
687 vf = vacfilewalk(parent, vd->elem);
688 if(vf == nil)
689 return 0;
690 vacfilegetsize(vf, &size);
691 ext = malloc(size+1);
692 if(ext == nil)
693 return 0;
694 n = vacfileread(vf, ext, size, 0);
695 ext[size] = 0;
696 vacfiledecref(vf);
697 if(vd->mode & ModeLink){
698 dir.qid.type |= QTSYMLINK;
699 dir.mode |= DMSYMLINK;
701 if(vd->mode & ModeDevice)
702 dir.mode |= DMDEVICE;
703 if(vd->mode & ModeNamedPipe)
704 dir.mode |= DMNAMEDPIPE;
706 #endif
708 dir.atime = vd->atime;
709 dir.mtime = vd->mtime;
710 dir.length = vd->size;
712 dir.name = vd->elem;
713 dir.uid = vd->uid;
714 dir.gid = vd->gid;
715 dir.muid = vd->mid;
716 #ifdef PLAN9PORT
717 dir.ext = ext;
718 dir.uidnum = atoi(vd->uid);
719 dir.gidnum = atoi(vd->gid);
720 #endif
722 ret = convD2Mu(&dir, p, np, dotu);
723 #ifdef PLAN9PORT
724 free(ext);
725 #endif
726 return ret;
729 int
730 vacdirread(Fid *f, char *p, long off, long cnt)
732 int i, n, nb;
733 VacDir vd;
735 /*
736 * special case of rewinding a directory
737 * otherwise ignore the offset
738 */
739 if(off == 0 && f->vde){
740 vdeclose(f->vde);
741 f->vde = nil;
744 if(f->vde == nil){
745 f->vde = vdeopen(f->file);
746 if(f->vde == nil)
747 return -1;
750 for(nb = 0; nb < cnt; nb += n) {
751 i = vderead(f->vde, &vd);
752 if(i < 0)
753 return -1;
754 if(i == 0)
755 break;
756 n = vacstat(f->file, &vd, (uchar*)p, cnt-nb);
757 if(n <= BIT16SZ) {
758 vdeunread(f->vde);
759 break;
761 vdcleanup(&vd);
762 p += n;
764 return nb;
767 Fid *
768 newfid(int fid)
770 Fid *f, *ff;
772 ff = 0;
773 for(f = fids; f; f = f->next)
774 if(f->fid == fid)
775 return f;
776 else if(!ff && !f->busy)
777 ff = f;
778 if(ff){
779 ff->fid = fid;
780 return ff;
782 f = vtmallocz(sizeof *f);
783 f->fid = fid;
784 f->next = fids;
785 fids = f;
786 return f;
789 void
790 io(void)
792 char *err;
793 int n;
795 for(;;){
796 n = read9pmsg(mfd[0], mdata, sizeof mdata);
797 if(n <= 0)
798 break;
799 if(convM2Su(mdata, n, &rhdr, dotu) != n)
800 sysfatal("convM2S conversion error");
802 if(dflag)
803 fprint(2, "vacfs:<-%F\n", &rhdr);
805 thdr.data = (char*)mdata + IOHDRSZ;
806 if(!fcalls[rhdr.type])
807 err = "bad fcall type";
808 else
809 err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
810 if(err){
811 thdr.type = Rerror;
812 thdr.ename = err;
813 #ifdef PLAN9PORT
814 thdr.errornum = 0;
815 #endif
816 }else{
817 thdr.type = rhdr.type + 1;
818 thdr.fid = rhdr.fid;
820 thdr.tag = rhdr.tag;
821 if(dflag)
822 fprint(2, "vacfs:->%F\n", &thdr);
823 n = convS2Mu(&thdr, mdata, messagesize, dotu);
824 if(n <= BIT16SZ)
825 sysfatal("convS2Mu conversion error");
826 if(err)
827 vtfree(err);
829 if(write(mfd[1], mdata, n) != n)
830 sysfatal("mount write: %r");
834 int
835 permf(VacFile *vf, char *user, int p)
837 VacDir dir;
838 ulong perm;
840 if(vacfilegetdir(vf, &dir))
841 return 0;
842 perm = dir.mode & 0777;
844 if(noperm)
845 goto Good;
846 if((p*Pother) & perm)
847 goto Good;
848 if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm))
849 goto Good;
850 if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm))
851 goto Good;
852 vdcleanup(&dir);
853 return 0;
854 Good:
855 vdcleanup(&dir);
856 return 1;
859 int
860 perm(Fid *f, int p)
862 return permf(f->file, f->user, p);
865 void
866 vacshutdown(void)
868 Fid *f;
870 for(f = fids; f; f = f->next) {
871 if(!f->busy)
872 continue;
873 rclunk(f);
876 vacfsclose(fs);
877 vthangup(conn);