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 Emip[] = "mount in progress";
15 char Enopsmt[] = "out of pseudo mount points";
16 char Enomem[] = "no memory";
17 char Eversion[] = "bad 9P2000 version";
18 char Ereadonly[] = "file system read only";
19 char Enoprocs[] = "out of processes";
20
21 ulong messagesize;
22 int readonly;
23
24 void
Xversion(Fsrpc * t)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 putsbuf(t);
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 putsbuf(t);
40 return;
41 }
42 rhdr.version = "9P2000";
43 rhdr.msize = t->work.msize;
44 reply(&t->work, &rhdr, 0);
45 putsbuf(t);
46 }
47
48 void
Xauth(Fsrpc * t)49 Xauth(Fsrpc *t)
50 {
51 Fcall rhdr;
52
53 reply(&t->work, &rhdr, "exportfs: authentication not required");
54 putsbuf(t);
55 }
56
57 void
Xflush(Fsrpc * t)58 Xflush(Fsrpc *t)
59 {
60 Fcall rhdr;
61 Fsrpc *w;
62 Proc *m;
63
64 for(m = Proclist; m != nil; m = m->next){
65 w = m->busy;
66 if(w == nil || w->work.tag != t->work.oldtag)
67 continue;
68
69 lock(m);
70 w = m->busy;
71 if(w != nil && w->work.tag == t->work.oldtag) {
72 w->flushtag = t->work.tag;
73 DEBUG(DFD, "\tset flushtag %d\n", t->work.tag);
74 postnote(PNPROC, m->pid, "flush");
75 unlock(m);
76 putsbuf(t);
77 return;
78 }
79 unlock(m);
80 }
81
82 reply(&t->work, &rhdr, 0);
83 DEBUG(DFD, "\tflush reply\n");
84 putsbuf(t);
85 }
86
87 void
Xattach(Fsrpc * t)88 Xattach(Fsrpc *t)
89 {
90 int i, nfd;
91 Fcall rhdr;
92 Fid *f;
93 char buf[128];
94
95 f = newfid(t->work.fid);
96 if(f == nil) {
97 reply(&t->work, &rhdr, Ebadfid);
98 putsbuf(t);
99 return;
100 }
101
102 if(srvfd >= 0){
103 if(psmpt == nil)
104 goto Nomount;
105 for(i=0; i<Npsmpt; i++)
106 if(psmap[i] == 0)
107 break;
108 if(i >= Npsmpt)
109 goto Nomount;
110 snprint(buf, sizeof(buf), "%d", i);
111 f->f = file(psmpt, buf);
112 if(f->f == nil)
113 goto Nomount;
114 snprint(buf, sizeof(buf), "/mnt/exportfs/%d", i);
115 nfd = dup(srvfd, -1);
116 if(amount(nfd, buf, MREPL|MCREATE, t->work.aname) < 0){
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 return;
135
136 Nomount:
137 reply(&t->work, &rhdr, Enopsmt);
138 freefid(t->work.fid);
139 putsbuf(t);
140 }
141
142 Fid*
clonefid(Fid * f,int new)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
Xwalk(Fsrpc * t)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
190 if(strcmp(t->work.wname[i], "..") == 0) {
191 if(f->f->parent != nil)
192 wf = f->f->parent;
193 else
194 wf = f->f;
195 wf->ref++;
196 }else{
197 wf = file(f->f, t->work.wname[i]);
198 if(wf == nil){
199 errstr(err, sizeof err);
200 e = err;
201 break;
202 }
203 }
204
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
Xclunk(Fsrpc * t)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
Xstat(Fsrpc * t)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
getiounit(int fd)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
Xcreate(Fsrpc * t)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
Xremove(Fsrpc * t)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(DFD, "\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
Xwstat(Fsrpc * t)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 && d.name[0]!=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 /*
435 * based on libthread's threadsetname, but drags in less library code.
436 * actually just sets the arguments displayed.
437 */
438 void
procsetname(char * fmt,...)439 procsetname(char *fmt, ...)
440 {
441 int fd;
442 char *cmdname;
443 char buf[128];
444 va_list arg;
445
446 va_start(arg, fmt);
447 cmdname = vsmprint(fmt, arg);
448 va_end(arg);
449 if (cmdname == nil)
450 return;
451 snprint(buf, sizeof buf, "#p/%d/args", getpid());
452 if((fd = open(buf, OWRITE)) >= 0){
453 write(fd, cmdname, strlen(cmdname)+1);
454 close(fd);
455 }
456 free(cmdname);
457 }
458
459 void
slave(Fsrpc * f)460 slave(Fsrpc *f)
461 {
462 static int nproc;
463 Proc *m, **l;
464 Fcall rhdr;
465 int omode;
466 int pid;
467
468 if(readonly){
469 switch(f->work.type){
470 case Twrite:
471 reply(&f->work, &rhdr, Ereadonly);
472 putsbuf(f);
473 return;
474 case Topen:
475 omode = f->work.mode & 3;
476 if(omode != OREAD && omode != OEXEC || (f->work.mode&(OTRUNC|ORCLOSE)) != 0){
477 reply(&f->work, &rhdr, Ereadonly);
478 putsbuf(f);
479 return;
480 }
481 }
482 }
483 for(;;) {
484 for(l = &Proclist; (m = *l) != nil; l = &m->next) {
485 if(m->busy != nil)
486 continue;
487
488 m->busy = f;
489 while(rendezvous(m, f) == (void*)~0)
490 ;
491
492 /* swept a slave proc */
493 if(f == nil){
494 *l = m->next;
495 free(m);
496 nproc--;
497 break;
498 }
499 f = nil;
500
501 /*
502 * as long as the number of slave procs
503 * is small, dont bother sweeping.
504 */
505 if(nproc < 16)
506 break;
507 }
508 if(f == nil)
509 return;
510
511 m = emallocz(sizeof(Proc));
512 pid = rfork(RFPROC|RFMEM|RFNOWAIT);
513 switch(pid) {
514 case -1:
515 reply(&f->work, &rhdr, Enoprocs);
516 putsbuf(f);
517 free(m);
518 return;
519
520 case 0:
521 if(local[0] != '\0'){
522 if(netdir[0] != '\0')
523 procsetname("%s: %s -> %s", netdir, local, remote);
524 else
525 procsetname("%s -> %s", local, remote);
526 }
527 blockingslave(m);
528 _exits(0);
529
530 default:
531 m->pid = pid;
532 m->next = Proclist;
533 Proclist = m;
534 nproc++;
535 }
536 }
537 }
538
539 void
blockingslave(Proc * m)540 blockingslave(Proc *m)
541 {
542 Fsrpc *p;
543 Fcall rhdr;
544
545 notify(flushaction);
546
547 for(;;) {
548 p = rendezvous(m, nil);
549 if(p == (void*)~0) /* Interrupted */
550 continue;
551 if(p == nil) /* Swept */
552 break;
553
554 DEBUG(DFD, "\tslave: %d %F\n", m->pid, &p->work);
555 if(p->flushtag != NOTAG)
556 goto flushme;
557
558 switch(p->work.type) {
559 case Tread:
560 slaveread(p);
561 break;
562
563 case Twrite:
564 slavewrite(p);
565 break;
566
567 case Topen:
568 slaveopen(p);
569 break;
570
571 default:
572 reply(&p->work, &rhdr, "exportfs: slave type error");
573 }
574 flushme:
575 lock(m);
576 m->busy = nil;
577 unlock(m);
578
579 /* no more flushes can come in now */
580 if(p->flushtag != NOTAG) {
581 p->work.type = Tflush;
582 p->work.tag = p->flushtag;
583 reply(&p->work, &rhdr, 0);
584 }
585 putsbuf(p);
586 }
587 }
588
589 int
openmount(int sfd)590 openmount(int sfd)
591 {
592 int p[2], fd, i, n;
593 char *arg[10], fdbuf[20], mbuf[20];
594 Dir *dir;
595
596 if(pipe(p) < 0)
597 return -1;
598
599 switch(rfork(RFPROC|RFMEM|RFNOWAIT|RFNAMEG|RFFDG|RFREND)){
600 case -1:
601 close(p[0]);
602 close(p[1]);
603 return -1;
604
605 default:
606 close(sfd);
607 close(p[0]);
608 return p[1];
609
610 case 0:
611 break;
612 }
613
614 dup(p[0], 0);
615 dup(p[0], 1);
616
617 /* close all remaining file descriptors except sfd */
618 if((fd = open("/fd", OREAD)) < 0)
619 _exits("open /fd failed");
620 n = dirreadall(fd, &dir);
621 for(i=0; i<n; i++){
622 if(strstr(dir[i].name, "ctl"))
623 continue;
624 fd = atoi(dir[i].name);
625 if(fd > 2 && fd != sfd)
626 close(fd);
627 }
628 free(dir);
629
630 arg[0] = argv0; /* "/bin/exportfs" */
631 snprint(fdbuf, sizeof fdbuf, "-S/fd/%d", sfd);
632 arg[1] = fdbuf;
633 snprint(mbuf, sizeof mbuf, "-m%lud", messagesize-IOHDRSZ);
634 arg[2] = mbuf;
635 arg[3] = nil;
636
637 exec(arg[0], arg);
638 _exits("whoops: exec failed");
639 return -1;
640 }
641
642 void
slaveopen(Fsrpc * p)643 slaveopen(Fsrpc *p)
644 {
645 char err[ERRMAX], *path;
646 Fcall *work, rhdr;
647 Fid *f;
648 Dir *d;
649
650 work = &p->work;
651
652 f = getfid(work->fid);
653 if(f == nil) {
654 reply(work, &rhdr, Ebadfid);
655 return;
656 }
657 if(f->fid >= 0) {
658 close(f->fid);
659 f->fid = -1;
660 }
661
662 path = makepath(f->f, "");
663 DEBUG(DFD, "\topen: %s %d\n", path, work->mode);
664 f->fid = open(path, work->mode);
665 free(path);
666 if(f->fid < 0 || (d = dirfstat(f->fid)) == nil) {
667 Error:
668 errstr(err, sizeof err);
669 reply(work, &rhdr, err);
670 return;
671 }
672 f->f->qid = d->qid;
673 free(d);
674 if(f->f->qid.type & QTMOUNT){ /* fork new exportfs for this */
675 f->fid = openmount(f->fid);
676 if(f->fid < 0)
677 goto Error;
678 }
679
680 DEBUG(DFD, "\topen: fd %d\n", f->fid);
681 f->mode = work->mode;
682 f->offset = 0;
683 rhdr.iounit = getiounit(f->fid);
684 rhdr.qid = f->f->qid;
685 reply(work, &rhdr, 0);
686 }
687
688 void
slaveread(Fsrpc * p)689 slaveread(Fsrpc *p)
690 {
691 Fid *f;
692 int n, r;
693 Fcall *work, rhdr;
694 char *data, err[ERRMAX];
695
696 work = &p->work;
697
698 f = getfid(work->fid);
699 if(f == nil) {
700 reply(work, &rhdr, Ebadfid);
701 return;
702 }
703
704 n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count;
705 data = malloc(n);
706 if(data == nil) {
707 reply(work, &rhdr, Enomem);
708 return;
709 }
710
711 /* can't just call pread, since directories must update the offset */
712 if(patternfile != nil && (f->f->qid.type&QTDIR))
713 r = preaddir(f, (uchar*)data, n, work->offset);
714 else
715 r = pread(f->fid, data, n, work->offset);
716 if(r < 0) {
717 free(data);
718 errstr(err, sizeof err);
719 reply(work, &rhdr, err);
720 return;
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
slavewrite(Fsrpc * p)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 == nil) {
742 reply(work, &rhdr, Ebadfid);
743 return;
744 }
745
746 n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count;
747 n = pwrite(f->fid, work->data, n, work->offset);
748 if(n < 0) {
749 errstr(err, sizeof err);
750 reply(work, &rhdr, err);
751 return;
752 }
753
754 DEBUG(DFD, "\twrite: %d bytes fd=%d\n", n, f->fid);
755
756 rhdr.count = n;
757 reply(work, &rhdr, 0);
758 }
759
760 void
reopen(Fid * f)761 reopen(Fid *f)
762 {
763 USED(f);
764 fatal("reopen");
765 }
766
767 void
flushaction(void * a,char * cause)768 flushaction(void *a, char *cause)
769 {
770 USED(a);
771 if(strncmp(cause, "sys:", 4) == 0 && !strstr(cause, "pipe")) {
772 fprint(2, "exportsrv: note: %s\n", cause);
773 exits("noted");
774 }
775 if(strncmp(cause, "kill", 4) == 0)
776 noted(NDFLT);
777
778 noted(NCONT);
779 }
780