]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vac/vacfs.c
exec(2): fix prototypes
[plan9front.git] / sys / src / cmd / vac / vacfs.c
1 #include "stdinc.h"
2 #include <fcall.h>
3 #include "vac.h"
4
5 #define convM2Su(a, b, c, d) convM2S(a, b, c)
6 #define convS2Mu(a, b, c, d) convS2M(a, b, c)
7 #define convM2Du(a, b, c, d) convM2D(a, b, c)
8 #define convD2Mu(a, b, c, d) convD2M(a, b, c)
9
10 typedef struct Fid Fid;
11
12 enum
13 {
14         Stacksize = 320 * 1024, /* was 32K */
15         OPERM   = 0x3           /* mask of all permission types in open mode */
16 };
17
18 struct Fid
19 {
20         short busy;
21         short open;
22         int fid;
23         char *user;
24         Qid qid;
25         VacFile *file;
26         VacDirEnum *vde;
27         Fid     *next;
28 };
29
30 enum
31 {
32         Pexec =         1,
33         Pwrite =        2,
34         Pread =         4,
35         Pother =        1,
36         Pgroup =        8,
37         Powner =        64
38 };
39
40 Fid     *fids;
41 uchar   *data;
42 int     mfd[2];
43 int     srvfd = -1;
44 char    *user;
45 uchar   mdata[8192+IOHDRSZ];
46 int messagesize = sizeof mdata;
47 Fcall   rhdr;
48 Fcall   thdr;
49 VacFs   *fs;
50 VtConn  *conn;
51 int     noperm;
52 int     dotu;
53 char *defmnt;
54
55 Fid *   newfid(int);
56 void    error(char*);
57 void    io(void);
58 void    vacshutdown(void);
59 void    usage(void);
60 int     perm(Fid*, int);
61 int     permf(VacFile*, char*, int);
62 ulong   getl(void *p);
63 void    init(char*, char*, long, int);
64 int     vacdirread(Fid *f, char *p, long off, long cnt);
65 int     vacstat(VacFile *parent, VacDir *vd, uchar *p, int np);
66 void    srv(void* a);
67
68
69 char    *rflush(Fid*), *rversion(Fid*),
70         *rauth(Fid*), *rattach(Fid*), *rwalk(Fid*),
71         *ropen(Fid*), *rcreate(Fid*),
72         *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
73         *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
74
75 char    *(*fcalls[Tmax])(Fid*);
76
77 void
78 initfcalls(void)
79 {
80         fcalls[Tflush]= rflush;
81         fcalls[Tversion]=       rversion;
82         fcalls[Tattach]=        rattach;
83         fcalls[Tauth]=          rauth;
84         fcalls[Twalk]=          rwalk;
85         fcalls[Topen]=          ropen;
86         fcalls[Tcreate]=        rcreate;
87         fcalls[Tread]=          rread;
88         fcalls[Twrite]= rwrite;
89         fcalls[Tclunk]= rclunk;
90         fcalls[Tremove]=        rremove;
91         fcalls[Tstat]=          rstat;
92         fcalls[Twstat]= rwstat;
93 }
94
95 char    Eperm[] =       "permission denied";
96 char    Enotdir[] =     "not a directory";
97 char    Enotexist[] =   "file does not exist";
98 char    Einuse[] =      "file in use";
99 char    Eexist[] =      "file exists";
100 char    Enotowner[] =   "not owner";
101 char    Eisopen[] =     "file already open for I/O";
102 char    Excl[] =        "exclusive use file already open";
103 char    Ename[] =       "illegal name";
104 char    Erdonly[] =     "read only file system";
105 char    Eio[] =         "i/o error";
106 char    Eempty[] =      "directory is not empty";
107 char    Emode[] =       "illegal mode";
108
109 int dflag;
110
111 void
112 notifyf(void *a, char *s)
113 {
114         USED(a);
115         if(strncmp(s, "interrupt", 9) == 0)
116                 noted(NCONT);
117         noted(NDFLT);
118 }
119
120 void
121 threadmain(int argc, char *argv[])
122 {
123         char *defsrv, *srvname;
124         int p[2], fd;
125         int stdio;
126         char *host = nil;
127         long ncache;
128
129         stdio = 0;
130         ncache = 256;
131         fmtinstall('H', encodefmt);
132         fmtinstall('V', vtscorefmt);
133         fmtinstall('F', vtfcallfmt);
134         
135         defmnt = nil;
136         defsrv = nil;
137         ARGBEGIN{
138         case 'd':
139                 fmtinstall('F', fcallfmt);
140                 dflag = 1;
141                 break;
142         case 'c':
143                 ncache = atoi(EARGF(usage()));
144                 break;
145         case 'i':
146                 defmnt = nil;
147                 stdio = 1;
148                 mfd[0] = 0;
149                 mfd[1] = 1;
150                 break;
151         case 'h':
152                 host = EARGF(usage());
153                 break;
154         case 'S':
155                 defsrv = EARGF(usage());
156                 break;
157         case 's':
158                 defsrv = "vacfs";
159                 break;
160         case 'm':
161                 defmnt = EARGF(usage());
162                 break;
163         case 'p':
164                 noperm = 1;
165                 break;
166         case 'V':
167                 chattyventi = 1;
168                 break;
169         default:
170                 usage();
171         }ARGEND
172
173         if(argc != 1)
174                 usage();
175
176         if(defsrv == nil && defmnt == nil && !stdio)
177                 defmnt = "/n/vac";
178         if(stdio && defmnt)
179                 sysfatal("cannot use -m with -i");
180
181         initfcalls();
182
183         notify(notifyf);
184         user = getuser();
185
186         conn = vtdial(host);
187         if(conn == nil)
188                 sysfatal("could not connect to server: %r");
189
190         if(vtconnect(conn) < 0)
191                 sysfatal("vtconnect: %r");
192
193         fs = vacfsopen(conn, argv[0], VtOREAD, ncache);
194         if(fs == nil)
195                 sysfatal("vacfsopen: %r");
196
197         if(!stdio){
198                 if(pipe(p) < 0)
199                         sysfatal("pipe failed: %r");
200                 mfd[0] = p[0];
201                 mfd[1] = p[0];
202                 srvfd = p[1];
203                 if(defsrv){
204                         srvname = smprint("/srv/%s", defsrv);
205                         fd = create(srvname, OWRITE|ORCLOSE, 0666);
206                         if(fd < 0)
207                                 sysfatal("create %s: %r", srvname);
208                         if(fprint(fd, "%d", srvfd) < 0)
209                                 sysfatal("write %s: %r", srvname);
210                         free(srvname);
211                 }
212         }
213
214         procrfork(srv, 0, Stacksize, RFFDG|RFNAMEG|RFNOTEG);
215
216         if(!stdio){
217                 close(p[0]);
218                 if(defmnt){
219                         if(mount(srvfd, -1, defmnt, MREPL|MCREATE, "") < 0)
220                                 sysfatal("mount %s: %r", defmnt);
221                 }
222         }
223         threadexits(0);
224 }
225
226 void
227 srv(void *a)
228 {
229         USED(a);
230         io();
231         vacshutdown();
232 }
233
234 void
235 usage(void)
236 {
237         fprint(2, "usage: %s [-sd] [-h host] [-c ncache] [-m mountpoint] vacfile\n", argv0);
238         threadexitsall("usage");
239 }
240
241 char*
242 rversion(Fid *unused)
243 {
244         Fid *f;
245
246         USED(unused);
247
248         for(f = fids; f; f = f->next)
249                 if(f->busy)
250                         rclunk(f);
251
252         if(rhdr.msize < 256)
253                 return vtstrdup("version: message size too small");
254         messagesize = rhdr.msize;
255         if(messagesize > sizeof mdata)
256                 messagesize = sizeof mdata;
257         thdr.msize = messagesize;
258         if(strncmp(rhdr.version, "9P2000", 6) != 0)
259                 return vtstrdup("unrecognized 9P version");
260         thdr.version = "9P2000";
261         if(strncmp(rhdr.version, "9P2000.u", 8) == 0){
262                 dotu = 1;
263                 thdr.version = "9P2000.u";
264         }
265         return nil;
266 }
267
268 char*
269 rflush(Fid *f)
270 {
271         USED(f);
272         return 0;
273 }
274
275 char*
276 rauth(Fid *f)
277 {
278         USED(f);
279         return vtstrdup("vacfs: authentication not required");
280 }
281
282 char*
283 rattach(Fid *f)
284 {
285         /* no authentication for the momment */
286         VacFile *file;
287         char err[80];
288
289         file = vacfsgetroot(fs);
290         if(file == nil) {
291                 rerrstr(err, sizeof err);
292                 return vtstrdup(err);
293         }
294
295         f->busy = 1;
296         f->file = file;
297         f->qid.path = vacfilegetid(f->file);
298         f->qid.vers = 0;
299         f->qid.type = QTDIR;
300         thdr.qid = f->qid;
301         if(rhdr.uname[0])
302                 f->user = vtstrdup(rhdr.uname);
303         else
304                 f->user = "none";
305         return 0;
306 }
307
308 char*
309 rwalk(Fid *f)
310 {
311         VacFile *file, *nfile;
312         Fid *nf;
313         int nqid, nwname;
314         Qid qid;
315         char *err = nil;
316
317         if(f->busy == 0)
318                 return Enotexist;
319         nf = nil;
320         if(rhdr.fid != rhdr.newfid){
321                 if(f->open)
322                         return vtstrdup(Eisopen);
323                 if(f->busy == 0)
324                         return vtstrdup(Enotexist);
325                 nf = newfid(rhdr.newfid);
326                 if(nf->busy)
327                         return vtstrdup(Eisopen);
328                 nf->busy = 1;
329                 nf->open = 0;
330                 nf->qid = f->qid;
331                 nf->file = vacfileincref(f->file);
332                 nf->user = vtstrdup(f->user);
333                 f = nf;
334         }
335
336         nwname = rhdr.nwname;
337
338         /* easy case */
339         if(nwname == 0) {
340                 thdr.nwqid = 0;
341                 return 0;
342         }
343
344         file = f->file;
345         vacfileincref(file);
346         qid = f->qid;
347
348         for(nqid = 0; nqid < nwname; nqid++){
349                 if((qid.type & QTDIR) == 0){
350                         err = Enotdir;
351                         break;
352                 }
353                 if(!permf(file, f->user, Pexec)) {
354                         err = Eperm;
355                         break;
356                 }
357                 nfile = vacfilewalk(file, rhdr.wname[nqid]);
358                 if(nfile == nil)
359                         break;
360                 vacfiledecref(file);
361                 file = nfile;
362                 qid.type = QTFILE;
363                 if(vacfileisdir(file))
364                         qid.type = QTDIR;
365                 qid.vers = vacfilegetmcount(file);
366                 qid.path = vacfilegetid(file);
367                 thdr.wqid[nqid] = qid;
368         }
369
370         thdr.nwqid = nqid;
371
372         if(nqid == nwname){
373                 /* success */
374                 f->qid = thdr.wqid[nqid-1];
375                 vacfiledecref(f->file);
376                 f->file = file;
377                 return 0;
378         }
379
380         vacfiledecref(file);
381         if(nf != nil)
382                 rclunk(nf);
383
384         /* only error on the first element */
385         if(nqid == 0)
386                 return vtstrdup(err);
387
388         return 0;
389 }
390
391 char *
392 ropen(Fid *f)
393 {
394         int mode, trunc;
395
396         if(f->open)
397                 return vtstrdup(Eisopen);
398         if(!f->busy)
399                 return vtstrdup(Enotexist);
400
401         mode = rhdr.mode;
402         thdr.iounit = messagesize - IOHDRSZ;
403         if(f->qid.type & QTDIR){
404                 if(mode != OREAD)
405                         return vtstrdup(Eperm);
406                 if(!perm(f, Pread))
407                         return vtstrdup(Eperm);
408                 thdr.qid = f->qid;
409                 f->vde = nil;
410                 f->open = 1;
411                 return 0;
412         }
413         if(mode & ORCLOSE)
414                 return vtstrdup(Erdonly);
415         trunc = mode & OTRUNC;
416         mode &= OPERM;
417         if(mode==OWRITE || mode==ORDWR || trunc)
418                 if(!perm(f, Pwrite))
419                         return vtstrdup(Eperm);
420         if(mode==OREAD || mode==ORDWR)
421                 if(!perm(f, Pread))
422                         return vtstrdup(Eperm);
423         if(mode==OEXEC)
424                 if(!perm(f, Pexec))
425                         return vtstrdup(Eperm);
426         thdr.qid = f->qid;
427         thdr.iounit = messagesize - IOHDRSZ;
428         f->open = 1;
429         return 0;
430 }
431
432 char*
433 rcreate(Fid* fid)
434 {
435         VacFile *vf;
436         ulong mode;
437
438         if(fid->open)
439                 return vtstrdup(Eisopen);
440         if(!fid->busy)
441                 return vtstrdup(Enotexist);
442         if(fs->mode & ModeSnapshot)
443                 return vtstrdup(Erdonly);
444         vf = fid->file;
445         if(!vacfileisdir(vf))
446                 return vtstrdup(Enotdir);
447         if(!permf(vf, fid->user, Pwrite))
448                 return vtstrdup(Eperm);
449
450         mode = rhdr.perm & 0777;
451
452         if(rhdr.perm & DMDIR){
453                 if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))
454                         return vtstrdup(Emode);
455                 switch(rhdr.mode & OPERM){
456                 default:
457                         return vtstrdup(Emode);
458                 case OEXEC:
459                 case OREAD:
460                         break;
461                 case OWRITE:
462                 case ORDWR:
463                         return vtstrdup(Eperm);
464                 }
465                 mode |= ModeDir;
466         }
467         vf = vacfilecreate(vf, rhdr.name, mode);
468         if(vf == nil) {
469                 char err[80];
470                 rerrstr(err, sizeof err);
471
472                 return vtstrdup(err);
473         }
474
475         vacfiledecref(fid->file);
476
477         fid->file = vf;
478         fid->qid.type = QTFILE;
479         if(vacfileisdir(vf))
480                 fid->qid.type = QTDIR;
481         fid->qid.vers = vacfilegetmcount(vf);
482         fid->qid.path = vacfilegetid(vf);
483
484         thdr.qid = fid->qid;
485         thdr.iounit = messagesize - IOHDRSZ;
486
487         return 0;
488 }
489
490 char*
491 rread(Fid *f)
492 {
493         char *buf;
494         vlong off;
495         int cnt;
496         VacFile *vf;
497         char err[80];
498         int n;
499
500         if(!f->busy)
501                 return vtstrdup(Enotexist);
502         vf = f->file;
503         thdr.count = 0;
504         off = rhdr.offset;
505         buf = thdr.data;
506         cnt = rhdr.count;
507         if(f->qid.type & QTDIR)
508                 n = vacdirread(f, buf, off, cnt);
509         else if(vacfilegetmode(f->file)&ModeDevice)
510                 return vtstrdup("device");
511         else if(vacfilegetmode(f->file)&ModeLink)
512                 return vtstrdup("symbolic link");
513         else if(vacfilegetmode(f->file)&ModeNamedPipe)
514                 return vtstrdup("named pipe");
515         else
516                 n = vacfileread(vf, buf, cnt, off);
517         if(n < 0) {
518                 rerrstr(err, sizeof err);
519                 return vtstrdup(err);
520         }
521         thdr.count = n;
522         return 0;
523 }
524
525 char*
526 rwrite(Fid *f)
527 {
528         USED(f);
529         return vtstrdup(Erdonly);
530 }
531
532 char *
533 rclunk(Fid *f)
534 {
535         f->busy = 0;
536         f->open = 0;
537         vtfree(f->user);
538         f->user = nil;
539         if(f->file)
540                 vacfiledecref(f->file);
541         f->file = nil;
542         vdeclose(f->vde);
543         f->vde = nil;
544         return 0;
545 }
546
547 char *
548 rremove(Fid *f)
549 {
550         VacFile *vf, *vfp;
551         char errbuf[80];
552         char *err = nil;
553
554         if(!f->busy)
555                 return vtstrdup(Enotexist);
556         vf = f->file;
557         vfp = vacfilegetparent(vf);
558
559         if(!permf(vfp, f->user, Pwrite)) {
560                 err = Eperm;
561                 goto Exit;
562         }
563
564         if(!vacfileremove(vf)) {
565                 rerrstr(errbuf, sizeof errbuf);
566                 err = errbuf;
567         }
568
569 Exit:
570         vacfiledecref(vfp);
571         rclunk(f);
572         return vtstrdup(err);
573 }
574
575 char *
576 rstat(Fid *f)
577 {
578         VacDir dir;
579         static uchar statbuf[1024];
580         VacFile *parent;
581         
582         if(!f->busy)
583                 return vtstrdup(Enotexist);
584         parent = vacfilegetparent(f->file);
585         vacfilegetdir(f->file, &dir);
586         thdr.stat = statbuf;
587         thdr.nstat = vacstat(parent, &dir, thdr.stat, sizeof statbuf);
588         vdcleanup(&dir);
589         vacfiledecref(parent);
590         return 0;
591 }
592
593 char *
594 rwstat(Fid *f)
595 {
596         if(!f->busy)
597                 return vtstrdup(Enotexist);
598         return vtstrdup(Erdonly);
599 }
600
601 int
602 vacstat(VacFile *parent, VacDir *vd, uchar *p, int np)
603 {
604         int ret;
605         Dir dir;
606
607         memset(&dir, 0, sizeof(dir));
608
609         dir.qid.path = vd->qid + vacfilegetqidoffset(parent);
610         if(vd->qidspace)
611                 dir.qid.path += vd->qidoffset;
612         dir.qid.vers = vd->mcount;
613         dir.mode = vd->mode & 0777;
614         if(vd->mode & ModeAppend){
615                 dir.qid.type |= QTAPPEND;
616                 dir.mode |= DMAPPEND;
617         }
618         if(vd->mode & ModeExclusive){
619                 dir.qid.type |= QTEXCL;
620                 dir.mode |= DMEXCL;
621         }
622         if(vd->mode & ModeDir){
623                 dir.qid.type |= QTDIR;
624                 dir.mode |= DMDIR;
625         }
626         
627         
628         dir.atime = vd->atime;
629         dir.mtime = vd->mtime;
630         dir.length = vd->size;
631
632         dir.name = vd->elem;
633         dir.uid = vd->uid;
634         dir.gid = vd->gid;
635         dir.muid = vd->mid;
636
637         ret = convD2Mu(&dir, p, np, dotu);
638         return ret;
639 }
640
641 int
642 vacdirread(Fid *f, char *p, long off, long cnt)
643 {
644         int i, n, nb;
645         VacDir vd;
646
647         /*
648          * special case of rewinding a directory
649          * otherwise ignore the offset
650          */
651         if(off == 0 && f->vde){
652                 vdeclose(f->vde);
653                 f->vde = nil;
654         }
655
656         if(f->vde == nil){
657                 f->vde = vdeopen(f->file);
658                 if(f->vde == nil)
659                         return -1;
660         }
661
662         for(nb = 0; nb < cnt; nb += n) {
663                 i = vderead(f->vde, &vd);
664                 if(i < 0)
665                         return -1;
666                 if(i == 0)
667                         break;
668                 n = vacstat(f->file, &vd, (uchar*)p, cnt-nb);
669                 if(n <= BIT16SZ) {
670                         vdeunread(f->vde);
671                         break;
672                 }
673                 vdcleanup(&vd);
674                 p += n;
675         }
676         return nb;
677 }
678
679 Fid *
680 newfid(int fid)
681 {
682         Fid *f, *ff;
683
684         ff = 0;
685         for(f = fids; f; f = f->next)
686                 if(f->fid == fid)
687                         return f;
688                 else if(!ff && !f->busy)
689                         ff = f;
690         if(ff){
691                 ff->fid = fid;
692                 return ff;
693         }
694         f = vtmallocz(sizeof *f);
695         f->fid = fid;
696         f->next = fids;
697         fids = f;
698         return f;
699 }
700
701 void
702 io(void)
703 {
704         char *err;
705         int n;
706
707         for(;;){
708                 n = read9pmsg(mfd[0], mdata, sizeof mdata);
709                 if(n < 0)
710                         break;
711                 if(n == 0)
712                         continue;
713                 if(convM2Su(mdata, n, &rhdr, dotu) != n)
714                         sysfatal("convM2S conversion error");
715
716                 if(dflag)
717                         fprint(2, "vacfs:<-%F\n", &rhdr);
718
719                 thdr.data = (char*)mdata + IOHDRSZ;
720                 if(!fcalls[rhdr.type])
721                         err = "bad fcall type";
722                 else
723                         err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
724                 if(err){
725                         thdr.type = Rerror;
726                         thdr.ename = err;
727                 }else{
728                         thdr.type = rhdr.type + 1;
729                         thdr.fid = rhdr.fid;
730                 }
731                 thdr.tag = rhdr.tag;
732                 if(dflag)
733                         fprint(2, "vacfs:->%F\n", &thdr);
734                 n = convS2Mu(&thdr, mdata, messagesize, dotu);
735                 if(n <= BIT16SZ)
736                         sysfatal("convS2Mu conversion error");
737                 if(err)
738                         vtfree(err);
739
740                 if(write(mfd[1], mdata, n) != n)
741                         sysfatal("mount write: %r");
742         }
743 }
744
745 int
746 permf(VacFile *vf, char *user, int p)
747 {
748         VacDir dir;
749         ulong perm;
750
751         if(vacfilegetdir(vf, &dir))
752                 return 0;
753         perm = dir.mode & 0777;
754
755         if(noperm)
756                 goto Good;
757         if((p*Pother) & perm)
758                 goto Good;
759         if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm))
760                 goto Good;
761         if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm))
762                 goto Good;
763         vdcleanup(&dir);
764         return 0;
765 Good:
766         vdcleanup(&dir);
767         return 1;
768 }
769
770 int
771 perm(Fid *f, int p)
772 {
773         return permf(f->file, f->user, p);
774 }
775
776 void
777 vacshutdown(void)
778 {
779         Fid *f;
780
781         for(f = fids; f; f = f->next) {
782                 if(!f->busy)
783                         continue;
784                 rclunk(f);
785         }
786
787         vacfsclose(fs);
788         vthangup(conn);
789 }
790