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