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