]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/exportfs/exportsrv.c
libregexp: improve the transition to next available thread, instruction, and generation
[plan9front.git] / sys / src / cmd / exportfs / exportsrv.c
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #define Extern  extern
6 #include "exportfs.h"
7
8 extern char *netdir, *local, *remote;
9
10 char Ebadfid[] = "Bad fid";
11 char Enotdir[] = "Not a directory";
12 char Edupfid[] = "Fid already in use";
13 char Eopen[] = "Fid already opened";
14 char Exmnt[] = "Cannot .. past mount point";
15 char Emip[] = "Mount in progress";
16 char Enopsmt[] = "Out of pseudo mount points";
17 char Enomem[] = "No memory";
18 char Eversion[] = "Bad 9P2000 version";
19 char Ereadonly[] = "File system read only";
20 char Enoprocs[] = "Out of processes";
21
22 ulong messagesize;
23 int readonly;
24
25 void
26 Xversion(Fsrpc *t)
27 {
28         Fcall rhdr;
29
30         if(t->work.msize < 256){
31                 reply(&t->work, &rhdr, "version: message size too small");
32                 putsbuf(t);
33                 return;
34         }
35         if(t->work.msize > messagesize)
36                 t->work.msize = messagesize;
37         messagesize = t->work.msize;
38         if(strncmp(t->work.version, "9P2000", 6) != 0){
39                 reply(&t->work, &rhdr, Eversion);
40                 putsbuf(t);
41                 return;
42         }
43         rhdr.version = "9P2000";
44         rhdr.msize = t->work.msize;
45         reply(&t->work, &rhdr, 0);
46         putsbuf(t);
47 }
48
49 void
50 Xauth(Fsrpc *t)
51 {
52         Fcall rhdr;
53
54         reply(&t->work, &rhdr, "exportfs: authentication not required");
55         putsbuf(t);
56 }
57
58 void
59 Xflush(Fsrpc *t)
60 {
61         Fcall rhdr;
62         Fsrpc *w;
63         Proc *m;
64
65         for(m = Proclist; m != nil; m = m->next){
66                 w = m->busy;
67                 if(w == nil || w->work.tag != t->work.oldtag)
68                         continue;
69
70                 lock(m);
71                 w = m->busy;
72                 if(w != nil && w->work.tag == t->work.oldtag) {
73                         w->flushtag = t->work.tag;
74                         DEBUG(DFD, "\tset flushtag %d\n", t->work.tag);
75                         postnote(PNPROC, m->pid, "flush");
76                         unlock(m);
77                         putsbuf(t);
78                         return;
79                 }
80                 unlock(m);
81         }
82
83         reply(&t->work, &rhdr, 0);
84         DEBUG(DFD, "\tflush reply\n");
85         putsbuf(t);
86 }
87
88 void
89 Xattach(Fsrpc *t)
90 {
91         int i, nfd;
92         Fcall rhdr;
93         Fid *f;
94         char buf[128];
95
96         f = newfid(t->work.fid);
97         if(f == nil) {
98                 reply(&t->work, &rhdr, Ebadfid);
99                 putsbuf(t);
100                 return;
101         }
102
103         if(srvfd >= 0){
104                 if(psmpt == nil){
105                 Nomount:
106                         reply(&t->work, &rhdr, Enopsmt);
107                         freefid(t->work.fid);
108                         putsbuf(t);
109                         return;
110                 }
111                 for(i=0; i<Npsmpt; i++)
112                         if(psmap[i] == 0)
113                                 break;
114                 if(i >= Npsmpt)
115                         goto Nomount;
116                 sprint(buf, "%d", i);
117                 f->f = file(psmpt, buf);
118                 if(f->f == nil)
119                         goto Nomount;
120                 sprint(buf, "/mnt/exportfs/%d", i);
121                 nfd = dup(srvfd, -1);
122                 if(amount(nfd, buf, MREPL|MCREATE, t->work.aname) < 0){
123                         errstr(buf, sizeof buf);
124                         reply(&t->work, &rhdr, buf);
125                         freefid(t->work.fid);
126                         putsbuf(t);
127                         close(nfd);
128                         return;
129                 }
130                 psmap[i] = 1;
131                 f->mid = i;
132         }else{
133                 f->f = root;
134                 f->f->ref++;
135         }
136
137         rhdr.qid = f->f->qid;
138         reply(&t->work, &rhdr, 0);
139         putsbuf(t);
140 }
141
142 Fid*
143 clonefid(Fid *f, int new)
144 {
145         Fid *n;
146
147         n = newfid(new);
148         if(n == nil) {
149                 n = getfid(new);
150                 if(n == nil)
151                         fatal("inconsistent fids");
152                 if(n->fid >= 0)
153                         close(n->fid);
154                 freefid(new);
155                 n = newfid(new);
156                 if(n == nil)
157                         fatal("inconsistent fids2");
158         }
159         n->f = f->f;
160         n->f->ref++;
161         return n;
162 }
163
164 void
165 Xwalk(Fsrpc *t)
166 {
167         char err[ERRMAX], *e;
168         Fcall rhdr;
169         Fid *f, *nf;
170         File *wf;
171         int i;
172
173         f = getfid(t->work.fid);
174         if(f == nil) {
175                 reply(&t->work, &rhdr, Ebadfid);
176                 putsbuf(t);
177                 return;
178         }
179
180         nf = nil;
181         if(t->work.newfid != t->work.fid){
182                 nf = clonefid(f, t->work.newfid);
183                 f = nf;
184         }
185
186         rhdr.nwqid = 0;
187         e = nil;
188         for(i=0; i<t->work.nwname; i++){
189                 if(i == MAXWELEM){
190                         e = "Too many path elements";
191                         break;
192                 }
193
194                 if(strcmp(t->work.wname[i], "..") == 0) {
195                         if(f->f->parent == nil) {
196                                 e = Exmnt;
197                                 break;
198                         }
199                         wf = f->f->parent;
200                         wf->ref++;
201                         goto Accept;
202                 }
203         
204                 wf = file(f->f, t->work.wname[i]);
205                 if(wf == nil){
206                         errstr(err, sizeof err);
207                         e = err;
208                         break;
209                 }
210     Accept:
211                 freefile(f->f);
212                 rhdr.wqid[rhdr.nwqid++] = wf->qid;
213                 f->f = wf;
214                 continue;
215         }
216
217         if(nf!=nil && (e!=nil || rhdr.nwqid!=t->work.nwname))
218                 freefid(t->work.newfid);
219         if(rhdr.nwqid > 0)
220                 e = nil;
221         reply(&t->work, &rhdr, e);
222         putsbuf(t);
223 }
224
225 void
226 Xclunk(Fsrpc *t)
227 {
228         Fcall rhdr;
229         Fid *f;
230
231         f = getfid(t->work.fid);
232         if(f == nil) {
233                 reply(&t->work, &rhdr, Ebadfid);
234                 putsbuf(t);
235                 return;
236         }
237
238         if(f->fid >= 0)
239                 close(f->fid);
240
241         freefid(t->work.fid);
242         reply(&t->work, &rhdr, 0);
243         putsbuf(t);
244 }
245
246 void
247 Xstat(Fsrpc *t)
248 {
249         char err[ERRMAX], *path;
250         Fcall rhdr;
251         Fid *f;
252         Dir *d;
253         int s;
254         uchar *statbuf;
255
256         f = getfid(t->work.fid);
257         if(f == nil) {
258                 reply(&t->work, &rhdr, Ebadfid);
259                 putsbuf(t);
260                 return;
261         }
262         if(f->fid >= 0)
263                 d = dirfstat(f->fid);
264         else {
265                 path = makepath(f->f, "");
266                 d = dirstat(path);
267                 free(path);
268         }
269
270         if(d == nil) {
271                 errstr(err, sizeof err);
272                 reply(&t->work, &rhdr, err);
273                 putsbuf(t);
274                 return;
275         }
276
277         d->qid.path = f->f->qidt->uniqpath;
278         s = sizeD2M(d);
279         statbuf = emallocz(s);
280         s = convD2M(d, statbuf, s);
281         free(d);
282         rhdr.nstat = s;
283         rhdr.stat = statbuf;
284         reply(&t->work, &rhdr, 0);
285         free(statbuf);
286         putsbuf(t);
287 }
288
289 static int
290 getiounit(int fd)
291 {
292         int n;
293
294         n = iounit(fd);
295         if(n > messagesize-IOHDRSZ)
296                 n = messagesize-IOHDRSZ;
297         return n;
298 }
299
300 void
301 Xcreate(Fsrpc *t)
302 {
303         char err[ERRMAX], *path;
304         Fcall rhdr;
305         Fid *f;
306         File *nf;
307
308         if(readonly) {
309                 reply(&t->work, &rhdr, Ereadonly);
310                 putsbuf(t);
311                 return;
312         }
313         f = getfid(t->work.fid);
314         if(f == nil) {
315                 reply(&t->work, &rhdr, Ebadfid);
316                 putsbuf(t);
317                 return;
318         }
319         
320
321         path = makepath(f->f, t->work.name);
322         f->fid = create(path, t->work.mode, t->work.perm);
323         free(path);
324         if(f->fid < 0) {
325                 errstr(err, sizeof err);
326                 reply(&t->work, &rhdr, err);
327                 putsbuf(t);
328                 return;
329         }
330
331         nf = file(f->f, t->work.name);
332         if(nf == nil) {
333                 errstr(err, sizeof err);
334                 reply(&t->work, &rhdr, err);
335                 putsbuf(t);
336                 return;
337         }
338
339         f->mode = t->work.mode;
340         freefile(f->f);
341         f->f = nf;
342         rhdr.qid = f->f->qid;
343         rhdr.iounit = getiounit(f->fid);
344         reply(&t->work, &rhdr, 0);
345         putsbuf(t);
346 }
347
348 void
349 Xremove(Fsrpc *t)
350 {
351         char err[ERRMAX], *path;
352         Fcall rhdr;
353         Fid *f;
354
355         if(readonly) {
356                 reply(&t->work, &rhdr, Ereadonly);
357                 putsbuf(t);
358                 return;
359         }
360         f = getfid(t->work.fid);
361         if(f == nil) {
362                 reply(&t->work, &rhdr, Ebadfid);
363                 putsbuf(t);
364                 return;
365         }
366
367         path = makepath(f->f, "");
368         DEBUG(DFD, "\tremove: %s\n", path);
369         if(remove(path) < 0) {
370                 free(path);
371                 errstr(err, sizeof err);
372                 reply(&t->work, &rhdr, err);
373                 putsbuf(t);
374                 return;
375         }
376         free(path);
377
378         f->f->inval = 1;
379         if(f->fid >= 0)
380                 close(f->fid);
381         freefid(t->work.fid);
382
383         reply(&t->work, &rhdr, 0);
384         putsbuf(t);
385 }
386
387 void
388 Xwstat(Fsrpc *t)
389 {
390         char err[ERRMAX], *path;
391         Fcall rhdr;
392         Fid *f;
393         int s;
394         char *strings;
395         Dir d;
396
397         if(readonly) {
398                 reply(&t->work, &rhdr, Ereadonly);
399                 putsbuf(t);
400                 return;
401         }
402         f = getfid(t->work.fid);
403         if(f == nil) {
404                 reply(&t->work, &rhdr, Ebadfid);
405                 putsbuf(t);
406                 return;
407         }
408         strings = emallocz(t->work.nstat);      /* ample */
409         if(convM2D(t->work.stat, t->work.nstat, &d, strings) <= BIT16SZ){
410                 rerrstr(err, sizeof err);
411                 reply(&t->work, &rhdr, err);
412                 putsbuf(t);
413                 free(strings);
414                 return;
415         }
416
417         if(f->fid >= 0)
418                 s = dirfwstat(f->fid, &d);
419         else {
420                 path = makepath(f->f, "");
421                 s = dirwstat(path, &d);
422                 free(path);
423         }
424         if(s < 0) {
425                 rerrstr(err, sizeof err);
426                 reply(&t->work, &rhdr, err);
427         }
428         else {
429                 /* wstat may really be rename */
430                 if(strcmp(d.name, f->f->name)!=0 && strcmp(d.name, "")!=0){
431                         free(f->f->name);
432                         f->f->name = estrdup(d.name);
433                 }
434                 reply(&t->work, &rhdr, 0);
435         }
436         free(strings);
437         putsbuf(t);
438 }
439
440 /*
441  * based on libthread's threadsetname, but drags in less library code.
442  * actually just sets the arguments displayed.
443  */
444 void
445 procsetname(char *fmt, ...)
446 {
447         int fd;
448         char *cmdname;
449         char buf[128];
450         va_list arg;
451
452         va_start(arg, fmt);
453         cmdname = vsmprint(fmt, arg);
454         va_end(arg);
455         if (cmdname == nil)
456                 return;
457         snprint(buf, sizeof buf, "#p/%d/args", getpid());
458         if((fd = open(buf, OWRITE)) >= 0){
459                 write(fd, cmdname, strlen(cmdname)+1);
460                 close(fd);
461         }
462         free(cmdname);
463 }
464
465 void
466 slave(Fsrpc *f)
467 {
468         static int nproc;
469         Proc *m, **l;
470         Fcall rhdr;
471         int pid;
472
473         if(readonly){
474                 switch(f->work.type){
475                 case Twrite:
476                         reply(&f->work, &rhdr, Ereadonly);
477                         putsbuf(f);
478                         return;
479                 case Topen:
480                         if((f->work.mode&3) == OWRITE || (f->work.mode&(OTRUNC|ORCLOSE))){
481                                 reply(&f->work, &rhdr, Ereadonly);
482                                 putsbuf(f);
483                                 return;
484                         }
485                 }
486         }
487         for(;;) {
488                 for(l = &Proclist; (m = *l) != nil; l = &m->next) {
489                         if(m->busy != nil)
490                                 continue;
491
492                         m->busy = f;
493                         while(rendezvous(m, f) == (void*)~0)
494                                 ;
495
496                         /* swept a slave proc */
497                         if(f == nil){
498                                 *l = m->next;
499                                 free(m);
500                                 nproc--;
501                                 break;
502                         }
503                         f = nil;
504
505                         /*
506                          * as long as the number of slave procs
507                          * is small, dont bother sweeping.
508                          */
509                         if(nproc < 16)
510                                 break;
511                 }
512                 if(f == nil)
513                         return;
514
515                 m = emallocz(sizeof(Proc));
516                 pid = rfork(RFPROC|RFMEM|RFNOWAIT);
517                 switch(pid) {
518                 case -1:
519                         reply(&f->work, &rhdr, Enoprocs);
520                         putsbuf(f);
521                         free(m);
522                         return;
523
524                 case 0:
525                         if (local[0] != '\0')
526                                 if (netdir[0] != '\0')
527                                         procsetname("%s: %s -> %s", netdir, 
528                                                 local, remote);
529                                 else
530                                         procsetname("%s -> %s", local, remote);
531                         blockingslave(m);
532                         _exits(0);
533
534                 default:
535                         m->pid = pid;
536                         m->next = Proclist;
537                         Proclist = m;
538                         nproc++;
539                 }
540         }
541 }
542
543 void
544 blockingslave(Proc *m)
545 {
546         Fsrpc *p;
547         Fcall rhdr;
548
549         notify(flushaction);
550
551         for(;;) {
552                 p = rendezvous(m, nil);
553                 if(p == (void*)~0)      /* Interrupted */
554                         continue;
555                 if(p == nil)            /* Swept */
556                         break;
557
558                 DEBUG(DFD, "\tslave: %d %F\n", m->pid, &p->work);
559                 if(p->flushtag != NOTAG)
560                         goto flushme;
561
562                 switch(p->work.type) {
563                 case Tread:
564                         slaveread(p);
565                         break;
566
567                 case Twrite:
568                         slavewrite(p);
569                         break;
570
571                 case Topen:
572                         slaveopen(p);
573                         break;
574
575                 default:
576                         reply(&p->work, &rhdr, "exportfs: slave type error");
577                 }
578 flushme:
579                 lock(m);
580                 m->busy = nil;
581                 unlock(m);
582
583                 /* no more flushes can come in now */
584                 if(p->flushtag != NOTAG) {
585                         p->work.type = Tflush;
586                         p->work.tag = p->flushtag;
587                         reply(&p->work, &rhdr, 0);
588                 }
589                 putsbuf(p);
590         }
591 }
592
593 int
594 openmount(int sfd)
595 {
596         int p[2], fd, i, n;
597         char *arg[10], fdbuf[20], mbuf[20];
598         Dir *dir;
599
600         if(pipe(p) < 0)
601                 return -1;
602
603         switch(rfork(RFPROC|RFMEM|RFNOWAIT|RFNAMEG|RFFDG|RFREND)){
604         case -1:
605                 close(p[0]);
606                 close(p[1]);
607                 return -1;
608
609         default:
610                 close(sfd);
611                 close(p[0]);
612                 return p[1];
613
614         case 0:
615                 break;
616         }
617
618         dup(p[0], 0);
619         dup(p[0], 1);
620
621         /* close all remaining file descriptors except sfd */
622         if((fd = open("/fd", OREAD)) < 0)
623                 _exits("open /fd failed");
624         n = dirreadall(fd, &dir);
625         for(i=0; i<n; i++){
626                 if(strstr(dir[i].name, "ctl"))
627                         continue;
628                 fd = atoi(dir[i].name);
629                 if(fd > 2 && fd != sfd)
630                         close(fd);
631         }
632         free(dir);
633
634         arg[0] = argv0; /* "/bin/exportfs" */
635         snprint(fdbuf, sizeof fdbuf, "-S/fd/%d", sfd);
636         arg[1] = fdbuf;
637         snprint(mbuf, sizeof mbuf, "-m%lud", messagesize-IOHDRSZ);
638         arg[2] = mbuf;
639         arg[3] = nil;
640
641         exec(arg[0], arg);
642         arg[0] = "/bin/exportfs";
643         exec(arg[0], arg);
644         _exits("whoops: exec failed");  
645         return -1;
646 }
647
648 void
649 slaveopen(Fsrpc *p)
650 {
651         char err[ERRMAX], *path;
652         Fcall *work, rhdr;
653         Fid *f;
654         Dir *d;
655
656         work = &p->work;
657
658         f = getfid(work->fid);
659         if(f == nil) {
660                 reply(work, &rhdr, Ebadfid);
661                 return;
662         }
663         if(f->fid >= 0) {
664                 close(f->fid);
665                 f->fid = -1;
666         }
667         
668         path = makepath(f->f, "");
669         DEBUG(DFD, "\topen: %s %d\n", path, work->mode);
670         f->fid = open(path, work->mode);
671         free(path);
672         if(f->fid < 0 || (d = dirfstat(f->fid)) == nil) {
673         Error:
674                 errstr(err, sizeof err);
675                 reply(work, &rhdr, err);
676                 return;
677         }
678         f->f->qid = d->qid;
679         free(d);
680         if(f->f->qid.type & QTMOUNT){   /* fork new exportfs for this */
681                 f->fid = openmount(f->fid);
682                 if(f->fid < 0)
683                         goto Error;
684         }
685
686         DEBUG(DFD, "\topen: fd %d\n", f->fid);
687         f->mode = work->mode;
688         f->offset = 0;
689         rhdr.iounit = getiounit(f->fid);
690         rhdr.qid = f->f->qid;
691         reply(work, &rhdr, 0);
692 }
693
694 void
695 slaveread(Fsrpc *p)
696 {
697         Fid *f;
698         int n, r;
699         Fcall *work, rhdr;
700         char *data, err[ERRMAX];
701
702         work = &p->work;
703
704         f = getfid(work->fid);
705         if(f == nil) {
706                 reply(work, &rhdr, Ebadfid);
707                 return;
708         }
709
710         n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count;
711         data = malloc(n);
712         if(data == nil) {
713                 reply(work, &rhdr, Enomem);
714                 return;
715         }
716
717         /* can't just call pread, since directories must update the offset */
718         if(patternfile != nil && (f->f->qid.type&QTDIR))
719                 r = preaddir(f, (uchar*)data, n, work->offset);
720         else
721                 r = pread(f->fid, data, n, work->offset);
722         if(r < 0) {
723                 free(data);
724                 errstr(err, sizeof err);
725                 reply(work, &rhdr, err);
726                 return;
727         }
728         DEBUG(DFD, "\tread: fd=%d %d bytes\n", f->fid, r);
729
730         rhdr.data = data;
731         rhdr.count = r;
732         reply(work, &rhdr, 0);
733         free(data);
734 }
735
736 void
737 slavewrite(Fsrpc *p)
738 {
739         char err[ERRMAX];
740         Fcall *work, rhdr;
741         Fid *f;
742         int n;
743
744         work = &p->work;
745
746         f = getfid(work->fid);
747         if(f == nil) {
748                 reply(work, &rhdr, Ebadfid);
749                 return;
750         }
751
752         n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count;
753         n = pwrite(f->fid, work->data, n, work->offset);
754         if(n < 0) {
755                 errstr(err, sizeof err);
756                 reply(work, &rhdr, err);
757                 return;
758         }
759
760         DEBUG(DFD, "\twrite: %d bytes fd=%d\n", n, f->fid);
761
762         rhdr.count = n;
763         reply(work, &rhdr, 0);
764 }
765
766 void
767 reopen(Fid *f)
768 {
769         USED(f);
770         fatal("reopen");
771 }
772
773 void
774 flushaction(void *a, char *cause)
775 {
776         USED(a);
777         if(strncmp(cause, "sys:", 4) == 0 && !strstr(cause, "pipe")) {
778                 fprint(2, "exportsrv: note: %s\n", cause);
779                 exits("noted");
780         }
781         if(strncmp(cause, "kill", 4) == 0)
782                 noted(NDFLT);
783
784         noted(NCONT);
785 }