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
Xversion(Fsrpc * t)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
Xauth(Fsrpc * t)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
Xflush(Fsrpc * t)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
Xattach(Fsrpc * t)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*
clonefid(Fid * f,int new)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
Xwalk(Fsrpc * t)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
Xclunk(Fsrpc * t)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
Xstat(Fsrpc * t)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
getiounit(int fd)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
Xcreate(Fsrpc * t)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
Xremove(Fsrpc * t)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
Xwstat(Fsrpc * t)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
procsetname(char * fmt,...)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
slave(Fsrpc * f)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
blockingslave(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
openmount(int sfd)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
slaveopen(Fsrpc * p)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
slaveread(Fsrpc * p)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
slavewrite(Fsrpc * p)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
reopen(Fid * f)746 reopen(Fid *f)
747 {
748 USED(f);
749 fatal("reopen");
750 }
751
752 void
flushaction(void * a,char * cause)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 }
765