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