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];
238 #ifdef PLAN9PORT
239 USED(fd);
240 proccreate(srv, 0, 32 * 1024);
241 if(!stdio && post9pservice(p[1], defsrv, defmnt) < 0)
242 sysfatal("post9pservice");
243 #else
244 procrfork(srv, 0, 32 * 1024, RFFDG|RFNAMEG|RFNOTEG);
246 if(!stdio){
247 close(p[0]);
248 if(defsrv){
249 srvname = smprint("/srv/%s", defsrv);
250 fd = create(srvname, OWRITE|ORCLOSE, 0666);
251 if(fd < 0)
252 sysfatal("create %s: %r", srvname);
253 if(fprint(fd, "%d", srvfd) < 0)
254 sysfatal("write %s: %r", srvname);
255 free(srvname);
257 if(defmnt){
258 if(mount(srvfd, -1, defmnt, MREPL|MCREATE, "") < 0)
259 sysfatal("mount %s: %r", defmnt);
262 #endif
263 threadexits(0);
266 void
267 srv(void *a)
269 USED(a);
270 io();
271 vacshutdown();
274 void
275 usage(void)
277 fprint(2, "usage: %s [-sd] [-h host] [-c ncache] [-m mountpoint] vacfile\n", argv0);
278 threadexitsall("usage");
281 char*
282 rversion(Fid *unused)
284 Fid *f;
286 USED(unused);
288 for(f = fids; f; f = f->next)
289 if(f->busy)
290 rclunk(f);
292 if(rhdr.msize < 256)
293 return vtstrdup("version: message size too small");
294 messagesize = rhdr.msize;
295 if(messagesize > sizeof mdata)
296 messagesize = sizeof mdata;
297 thdr.msize = messagesize;
298 if(strncmp(rhdr.version, "9P2000", 6) != 0)
299 return vtstrdup("unrecognized 9P version");
300 thdr.version = "9P2000";
301 return nil;
304 char*
305 rflush(Fid *f)
307 USED(f);
308 return 0;
311 char*
312 rauth(Fid *f)
314 USED(f);
315 return vtstrdup("vacfs: authentication not required");
318 char*
319 rattach(Fid *f)
321 /* no authentication for the momment */
322 VacFile *file;
323 char err[80];
325 file = vacfsgetroot(fs);
326 if(file == nil) {
327 rerrstr(err, sizeof err);
328 return vtstrdup(err);
331 f->busy = 1;
332 f->file = file;
333 f->qid.path = vacfilegetid(f->file);
334 f->qid.vers = 0;
335 f->qid.type = QTDIR;
336 thdr.qid = f->qid;
337 if(rhdr.uname[0])
338 f->user = vtstrdup(rhdr.uname);
339 else
340 f->user = "none";
341 return 0;
344 char*
345 rwalk(Fid *f)
347 VacFile *file, *nfile;
348 Fid *nf;
349 int nqid, nwname;
350 Qid qid;
351 char *err = nil;
353 if(f->busy == 0)
354 return Enotexist;
355 nf = nil;
356 if(rhdr.fid != rhdr.newfid){
357 if(f->open)
358 return vtstrdup(Eisopen);
359 if(f->busy == 0)
360 return vtstrdup(Enotexist);
361 nf = newfid(rhdr.newfid);
362 if(nf->busy)
363 return vtstrdup(Eisopen);
364 nf->busy = 1;
365 nf->open = 0;
366 nf->qid = f->qid;
367 nf->file = vacfileincref(f->file);
368 nf->user = vtstrdup(f->user);
369 f = nf;
372 nwname = rhdr.nwname;
374 /* easy case */
375 if(nwname == 0) {
376 thdr.nwqid = 0;
377 return 0;
380 file = f->file;
381 vacfileincref(file);
382 qid = f->qid;
384 for(nqid = 0; nqid < nwname; nqid++){
385 if((qid.type & QTDIR) == 0){
386 err = Enotdir;
387 break;
389 if(!permf(file, f->user, Pexec)) {
390 err = Eperm;
391 break;
393 nfile = vacfilewalk(file, rhdr.wname[nqid]);
394 if(nfile == nil)
395 break;
396 vacfiledecref(file);
397 file = nfile;
398 qid.type = QTFILE;
399 if(vacfileisdir(file))
400 qid.type = QTDIR;
401 #ifdef PLAN9PORT
402 if(vacfilegetmode(file)&ModeLink)
403 qid.type = QTSYMLINK;
404 #endif
405 qid.vers = vacfilegetmcount(file);
406 qid.path = vacfilegetid(file);
407 thdr.wqid[nqid] = qid;
410 thdr.nwqid = nqid;
412 if(nqid == nwname){
413 /* success */
414 f->qid = thdr.wqid[nqid-1];
415 vacfiledecref(f->file);
416 f->file = file;
417 return 0;
420 vacfiledecref(file);
421 if(nf != nil)
422 rclunk(nf);
424 /* only error on the first element */
425 if(nqid == 0)
426 return vtstrdup(err);
428 return 0;
431 char *
432 ropen(Fid *f)
434 int mode, trunc;
436 if(f->open)
437 return vtstrdup(Eisopen);
438 if(!f->busy)
439 return vtstrdup(Enotexist);
441 mode = rhdr.mode;
442 thdr.iounit = messagesize - IOHDRSZ;
443 if(f->qid.type & QTDIR){
444 if(mode != OREAD)
445 return vtstrdup(Eperm);
446 if(!perm(f, Pread))
447 return vtstrdup(Eperm);
448 thdr.qid = f->qid;
449 f->vde = nil;
450 f->open = 1;
451 return 0;
453 if(mode & ORCLOSE)
454 return vtstrdup(Erdonly);
455 trunc = mode & OTRUNC;
456 mode &= OPERM;
457 if(mode==OWRITE || mode==ORDWR || trunc)
458 if(!perm(f, Pwrite))
459 return vtstrdup(Eperm);
460 if(mode==OREAD || mode==ORDWR)
461 if(!perm(f, Pread))
462 return vtstrdup(Eperm);
463 if(mode==OEXEC)
464 if(!perm(f, Pexec))
465 return vtstrdup(Eperm);
466 thdr.qid = f->qid;
467 thdr.iounit = messagesize - IOHDRSZ;
468 f->open = 1;
469 return 0;
472 char*
473 rcreate(Fid* fid)
475 VacFile *vf;
476 ulong mode;
478 if(fid->open)
479 return vtstrdup(Eisopen);
480 if(!fid->busy)
481 return vtstrdup(Enotexist);
482 if(fs->mode & ModeSnapshot)
483 return vtstrdup(Erdonly);
484 vf = fid->file;
485 if(!vacfileisdir(vf))
486 return vtstrdup(Enotdir);
487 if(!permf(vf, fid->user, Pwrite))
488 return vtstrdup(Eperm);
490 mode = rhdr.perm & 0777;
492 if(rhdr.perm & DMDIR){
493 if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))
494 return vtstrdup(Emode);
495 switch(rhdr.mode & OPERM){
496 default:
497 return vtstrdup(Emode);
498 case OEXEC:
499 case OREAD:
500 break;
501 case OWRITE:
502 case ORDWR:
503 return vtstrdup(Eperm);
505 mode |= ModeDir;
507 vf = vacfilecreate(vf, rhdr.name, mode);
508 if(vf == nil) {
509 char err[80];
510 rerrstr(err, sizeof err);
512 return vtstrdup(err);
515 vacfiledecref(fid->file);
517 fid->file = vf;
518 fid->qid.type = QTFILE;
519 if(vacfileisdir(vf))
520 fid->qid.type = QTDIR;
521 fid->qid.vers = vacfilegetmcount(vf);
522 fid->qid.path = vacfilegetid(vf);
524 thdr.qid = fid->qid;
525 thdr.iounit = messagesize - IOHDRSZ;
527 return 0;
530 char*
531 rread(Fid *f)
533 char *buf;
534 vlong off;
535 int cnt;
536 VacFile *vf;
537 char err[80];
538 int n;
540 if(!f->busy)
541 return vtstrdup(Enotexist);
542 vf = f->file;
543 thdr.count = 0;
544 off = rhdr.offset;
545 buf = thdr.data;
546 cnt = rhdr.count;
547 if(f->qid.type & QTDIR)
548 n = vacdirread(f, buf, off, cnt);
549 else if(vacfilegetmode(f->file)&ModeDevice)
550 return vtstrdup("device");
551 else if(vacfilegetmode(f->file)&ModeLink)
552 return vtstrdup("symbolic link");
553 else if(vacfilegetmode(f->file)&ModeNamedPipe)
554 return vtstrdup("named pipe");
555 else
556 n = vacfileread(vf, buf, cnt, off);
557 if(n < 0) {
558 rerrstr(err, sizeof err);
559 return vtstrdup(err);
561 thdr.count = n;
562 return 0;
565 char*
566 rwrite(Fid *f)
568 USED(f);
569 return vtstrdup(Erdonly);
572 char *
573 rclunk(Fid *f)
575 f->busy = 0;
576 f->open = 0;
577 vtfree(f->user);
578 f->user = nil;
579 if(f->file)
580 vacfiledecref(f->file);
581 f->file = nil;
582 vdeclose(f->vde);
583 f->vde = nil;
584 return 0;
587 char *
588 rremove(Fid *f)
590 VacFile *vf, *vfp;
591 char errbuf[80];
592 char *err = nil;
594 if(!f->busy)
595 return vtstrdup(Enotexist);
596 vf = f->file;
597 vfp = vacfilegetparent(vf);
599 if(!permf(vfp, f->user, Pwrite)) {
600 err = Eperm;
601 goto Exit;
604 if(!vacfileremove(vf)) {
605 rerrstr(errbuf, sizeof errbuf);
606 err = errbuf;
609 Exit:
610 vacfiledecref(vfp);
611 rclunk(f);
612 return vtstrdup(err);
615 char *
616 rstat(Fid *f)
618 VacDir dir;
619 static uchar statbuf[1024];
620 VacFile *parent;
622 if(!f->busy)
623 return vtstrdup(Enotexist);
624 parent = vacfilegetparent(f->file);
625 vacfilegetdir(f->file, &dir);
626 thdr.stat = statbuf;
627 thdr.nstat = vacstat(parent, &dir, thdr.stat, sizeof statbuf);
628 vdcleanup(&dir);
629 vacfiledecref(parent);
630 return 0;
633 char *
634 rwstat(Fid *f)
636 if(!f->busy)
637 return vtstrdup(Enotexist);
638 return vtstrdup(Erdonly);
641 int
642 vacstat(VacFile *parent, VacDir *vd, uchar *p, int np)
644 int ret;
645 Dir dir;
646 #ifdef PLAN9PORT
647 int n;
648 VacFile *vf;
649 uvlong size;
650 char *ext = nil;
651 #endif
653 memset(&dir, 0, sizeof(dir));
655 dir.qid.path = vd->qid + vacfilegetqidoffset(parent);
656 if(vd->qidspace)
657 dir.qid.path += vd->qidoffset;
658 dir.qid.vers = vd->mcount;
659 dir.mode = vd->mode & 0777;
660 if(vd->mode & ModeAppend){
661 dir.qid.type |= QTAPPEND;
662 dir.mode |= DMAPPEND;
664 if(vd->mode & ModeExclusive){
665 dir.qid.type |= QTEXCL;
666 dir.mode |= DMEXCL;
668 if(vd->mode & ModeDir){
669 dir.qid.type |= QTDIR;
670 dir.mode |= DMDIR;
673 #ifdef PLAN9PORT
674 if(vd->mode & (ModeLink|ModeDevice|ModeNamedPipe)){
675 vf = vacfilewalk(parent, vd->elem);
676 if(vf == nil)
677 return 0;
678 vacfilegetsize(vf, &size);
679 ext = malloc(size+1);
680 if(ext == nil)
681 return 0;
682 n = vacfileread(vf, ext, size, 0);
683 ext[size] = 0;
684 vacfiledecref(vf);
685 if(vd->mode & ModeLink){
686 dir.qid.type |= QTSYMLINK;
687 dir.mode |= DMSYMLINK;
689 if(vd->mode & ModeDevice)
690 dir.mode |= DMDEVICE;
691 if(vd->mode & ModeNamedPipe)
692 dir.mode |= DMNAMEDPIPE;
694 #endif
696 dir.atime = vd->atime;
697 dir.mtime = vd->mtime;
698 dir.length = vd->size;
700 dir.name = vd->elem;
701 dir.uid = vd->uid;
702 dir.gid = vd->gid;
703 dir.muid = vd->mid;
704 #ifdef PLAN9PORT
705 dir.ext = ext;
706 dir.uidnum = atoi(vd->uid);
707 dir.gidnum = atoi(vd->gid);
708 #endif
710 ret = convD2M(&dir, p, np);
711 #ifdef PLAN9PORT
712 free(ext);
713 #endif
714 return ret;
717 int
718 vacdirread(Fid *f, char *p, long off, long cnt)
720 int i, n, nb;
721 VacDir vd;
723 /*
724 * special case of rewinding a directory
725 * otherwise ignore the offset
726 */
727 if(off == 0 && f->vde){
728 vdeclose(f->vde);
729 f->vde = nil;
732 if(f->vde == nil){
733 f->vde = vdeopen(f->file);
734 if(f->vde == nil)
735 return -1;
738 for(nb = 0; nb < cnt; nb += n) {
739 i = vderead(f->vde, &vd);
740 if(i < 0)
741 return -1;
742 if(i == 0)
743 break;
744 n = vacstat(f->file, &vd, (uchar*)p, cnt-nb);
745 if(n <= BIT16SZ) {
746 vdeunread(f->vde);
747 break;
749 vdcleanup(&vd);
750 p += n;
752 return nb;
755 Fid *
756 newfid(int fid)
758 Fid *f, *ff;
760 ff = 0;
761 for(f = fids; f; f = f->next)
762 if(f->fid == fid)
763 return f;
764 else if(!ff && !f->busy)
765 ff = f;
766 if(ff){
767 ff->fid = fid;
768 return ff;
770 f = vtmallocz(sizeof *f);
771 f->fid = fid;
772 f->next = fids;
773 fids = f;
774 return f;
777 void
778 io(void)
780 char *err;
781 int n;
783 for(;;){
784 n = read9pmsg(mfd[0], mdata, sizeof mdata);
785 if(n <= 0)
786 break;
787 if(convM2S(mdata, n, &rhdr) != n)
788 sysfatal("convM2S conversion error");
790 if(dflag)
791 fprint(2, "vacfs:<-%F\n", &rhdr);
793 thdr.data = (char*)mdata + IOHDRSZ;
794 if(!fcalls[rhdr.type])
795 err = "bad fcall type";
796 else
797 err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
798 if(err){
799 thdr.type = Rerror;
800 thdr.ename = err;
801 #ifdef PLAN9PORT
802 thdr.errornum = 0;
803 #endif
804 }else{
805 thdr.type = rhdr.type + 1;
806 thdr.fid = rhdr.fid;
808 thdr.tag = rhdr.tag;
809 if(dflag)
810 fprint(2, "vacfs:->%F\n", &thdr);
811 n = convS2M(&thdr, mdata, messagesize);
812 if(n <= BIT16SZ)
813 sysfatal("convS2M conversion error");
814 if(err)
815 vtfree(err);
817 if(write(mfd[1], mdata, n) != n)
818 sysfatal("mount write: %r");
822 int
823 permf(VacFile *vf, char *user, int p)
825 VacDir dir;
826 ulong perm;
828 if(vacfilegetdir(vf, &dir))
829 return 0;
830 perm = dir.mode & 0777;
832 if(noperm)
833 goto Good;
834 if((p*Pother) & perm)
835 goto Good;
836 if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm))
837 goto Good;
838 if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm))
839 goto Good;
840 vdcleanup(&dir);
841 return 0;
842 Good:
843 vdcleanup(&dir);
844 return 1;
847 int
848 perm(Fid *f, int p)
850 return permf(f->file, f->user, p);
853 void
854 vacshutdown(void)
856 Fid *f;
858 for(f = fids; f; f = f->next) {
859 if(!f->busy)
860 continue;
861 rclunk(f);
864 vacfsclose(fs);
865 vthangup(conn);