1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #define Extern extern
6 #include "statfs.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 Enoauth[] = "iostats: Authentication failed";
14 char Ebadver[] = "Unrecognized 9P version";
15
16 int
okfile(char * s,int mode)17 okfile(char *s, int mode)
18 {
19 if(strncmp(s, "/fd/", 3) == 0){
20 /* 0, 1, and 2 we handle ourselves */
21 if(s[4]=='/' || atoi(s+4) > 2)
22 return 0;
23 return 1;
24 }
25 if(strncmp(s, "/net/ssl", 8) == 0)
26 return 0;
27 if(strncmp(s, "/net/tls", 8) == 0)
28 return 0;
29 if(strncmp(s, "/srv/", 5) == 0 && ((mode&3) == OWRITE || (mode&3) == ORDWR))
30 return 0;
31 return 1;
32 }
33
34 void
update(Rpc * rpc,vlong t)35 update(Rpc *rpc, vlong t)
36 {
37 vlong t2;
38
39 t2 = nsec();
40 t = t2 - t;
41 if(t < 0)
42 t = 0;
43
44 rpc->time += t;
45 if(t < rpc->lo)
46 rpc->lo = t;
47 if(t > rpc->hi)
48 rpc->hi = t;
49 }
50
51 void
Xversion(Fsrpc * r)52 Xversion(Fsrpc *r)
53 {
54 Fcall thdr;
55 vlong t;
56
57 t = nsec();
58
59 if(r->work.msize > IOHDRSZ+Maxfdata)
60 thdr.msize = IOHDRSZ+Maxfdata;
61 else
62 thdr.msize = r->work.msize;
63 myiounit = thdr.msize - IOHDRSZ;
64 if(strncmp(r->work.version, "9P2000", 6) != 0){
65 reply(&r->work, &thdr, Ebadver);
66 r->busy = 0;
67 return;
68 }
69 thdr.version = "9P2000";
70 /* BUG: should clunk all fids */
71 reply(&r->work, &thdr, 0);
72 r->busy = 0;
73
74 update(&stats->rpc[Tversion], t);
75 }
76
77 void
Xauth(Fsrpc * r)78 Xauth(Fsrpc *r)
79 {
80 Fcall thdr;
81 vlong t;
82
83 t = nsec();
84
85 reply(&r->work, &thdr, Enoauth);
86 r->busy = 0;
87
88 update(&stats->rpc[Tauth], t);
89 }
90
91 void
Xflush(Fsrpc * r)92 Xflush(Fsrpc *r)
93 {
94 Fsrpc *t, *e;
95 Fcall thdr;
96
97 e = &Workq[Nr_workbufs];
98
99 for(t = Workq; t < e; t++) {
100 if(t->work.tag == r->work.oldtag) {
101 DEBUG(2, "\tQ busy %d pid %p can %d\n", t->busy, t->pid, t->canint);
102 if(t->busy && t->pid) {
103 t->flushtag = r->work.tag;
104 DEBUG(2, "\tset flushtag %d\n", r->work.tag);
105 if(t->canint)
106 postnote(PNPROC, t->pid, "flush");
107 r->busy = 0;
108 return;
109 }
110 }
111 }
112
113 reply(&r->work, &thdr, 0);
114 DEBUG(2, "\tflush reply\n");
115 r->busy = 0;
116 }
117
118 void
Xattach(Fsrpc * r)119 Xattach(Fsrpc *r)
120 {
121 Fcall thdr;
122 Fid *f;
123 vlong t;
124
125 t = nsec();
126
127 f = newfid(r->work.fid);
128 if(f == 0) {
129 reply(&r->work, &thdr, Ebadfid);
130 r->busy = 0;
131 return;
132 }
133
134 f->f = root;
135 thdr.qid = f->f->qid;
136 reply(&r->work, &thdr, 0);
137 r->busy = 0;
138
139 update(&stats->rpc[Tattach], t);
140 }
141
142 void
Xwalk(Fsrpc * r)143 Xwalk(Fsrpc *r)
144 {
145 char errbuf[ERRMAX], *err;
146 Fcall thdr;
147 Fid *f, *n;
148 File *nf;
149 vlong t;
150 int i;
151
152 t = nsec();
153
154 f = getfid(r->work.fid);
155 if(f == 0) {
156 reply(&r->work, &thdr, Ebadfid);
157 r->busy = 0;
158 return;
159 }
160 n = nil;
161 if(r->work.newfid != r->work.fid){
162 n = newfid(r->work.newfid);
163 if(n == 0) {
164 reply(&r->work, &thdr, Edupfid);
165 r->busy = 0;
166 return;
167 }
168 n->f = f->f;
169 f = n; /* walk new guy */
170 }
171
172 thdr.nwqid = 0;
173 err = nil;
174 for(i=0; i<r->work.nwname; i++){
175 if(i >= MAXWELEM)
176 break;
177 if(strcmp(r->work.wname[i], "..") == 0) {
178 if(f->f->parent == 0) {
179 err = Exmnt;
180 break;
181 }
182 f->f = f->f->parent;
183 thdr.wqid[thdr.nwqid++] = f->f->qid;
184 continue;
185 }
186
187 nf = file(f->f, r->work.wname[i]);
188 if(nf == 0) {
189 errstr(errbuf, sizeof errbuf);
190 err = errbuf;
191 break;
192 }
193
194 f->f = nf;
195 thdr.wqid[thdr.nwqid++] = nf->qid;
196 continue;
197 }
198
199 if(err == nil && thdr.nwqid == 0 && r->work.nwname > 0)
200 err = "file does not exist";
201
202 if(n != nil && (err != 0 || thdr.nwqid < r->work.nwname)){
203 /* clunk the new fid, which is the one we walked */
204 freefid(n->nr);
205 }
206
207 if(thdr.nwqid > 0)
208 err = nil;
209 reply(&r->work, &thdr, err);
210 r->busy = 0;
211
212 update(&stats->rpc[Twalk], t);
213 }
214
215 void
Xclunk(Fsrpc * r)216 Xclunk(Fsrpc *r)
217 {
218 Fcall thdr;
219 Fid *f;
220 vlong t;
221 int fid;
222
223 t = nsec();
224
225 f = getfid(r->work.fid);
226 if(f == 0) {
227 reply(&r->work, &thdr, Ebadfid);
228 r->busy = 0;
229 return;
230 }
231
232 if(f->fid >= 0)
233 close(f->fid);
234
235 fid = r->work.fid;
236 reply(&r->work, &thdr, 0);
237 r->busy = 0;
238
239 update(&stats->rpc[Tclunk], t);
240
241 if(f->nread || f->nwrite)
242 fidreport(f);
243
244 freefid(fid);
245 }
246
247 void
Xstat(Fsrpc * r)248 Xstat(Fsrpc *r)
249 {
250 char err[ERRMAX], path[128];
251 uchar statbuf[STATMAX];
252 Fcall thdr;
253 Fid *f;
254 int s;
255 vlong t;
256
257 t = nsec();
258
259 f = getfid(r->work.fid);
260 if(f == 0) {
261 reply(&r->work, &thdr, Ebadfid);
262 r->busy = 0;
263 return;
264 }
265 makepath(path, f->f, "");
266 if(!okfile(path, -1)){
267 snprint(err, sizeof err, "iostats: can't simulate %s", path);
268 reply(&r->work, &thdr, err);
269 r->busy = 0;
270 return;
271 }
272
273 if(f->fid >= 0)
274 s = fstat(f->fid, statbuf, sizeof statbuf);
275 else
276 s = stat(path, statbuf, sizeof statbuf);
277
278 if(s < 0) {
279 errstr(err, sizeof err);
280 reply(&r->work, &thdr, err);
281 r->busy = 0;
282 return;
283 }
284 thdr.stat = statbuf;
285 thdr.nstat = s;
286 reply(&r->work, &thdr, 0);
287 r->busy = 0;
288
289 update(&stats->rpc[Tstat], t);
290 }
291
292 void
Xcreate(Fsrpc * r)293 Xcreate(Fsrpc *r)
294 {
295 char err[ERRMAX], path[128];
296 Fcall thdr;
297 Fid *f;
298 File *nf;
299 vlong t;
300
301 t = nsec();
302
303 f = getfid(r->work.fid);
304 if(f == 0) {
305 reply(&r->work, &thdr, Ebadfid);
306 r->busy = 0;
307 return;
308 }
309
310
311 makepath(path, f->f, r->work.name);
312 f->fid = create(path, r->work.mode, r->work.perm);
313 if(f->fid < 0) {
314 errstr(err, sizeof err);
315 reply(&r->work, &thdr, err);
316 r->busy = 0;
317 return;
318 }
319
320 nf = file(f->f, r->work.name);
321 if(nf == 0) {
322 errstr(err, sizeof err);
323 reply(&r->work, &thdr, err);
324 r->busy = 0;
325 return;
326 }
327
328 f->mode = r->work.mode;
329 f->f = nf;
330 thdr.iounit = myiounit;
331 thdr.qid = f->f->qid;
332 reply(&r->work, &thdr, 0);
333 r->busy = 0;
334
335 update(&stats->rpc[Tcreate], t);
336 }
337
338
339 void
Xremove(Fsrpc * r)340 Xremove(Fsrpc *r)
341 {
342 char err[ERRMAX], path[128];
343 Fcall thdr;
344 Fid *f;
345 vlong t;
346
347 t = nsec();
348
349 f = getfid(r->work.fid);
350 if(f == 0) {
351 reply(&r->work, &thdr, Ebadfid);
352 r->busy = 0;
353 return;
354 }
355
356 makepath(path, f->f, "");
357 DEBUG(2, "\tremove: %s\n", path);
358 if(remove(path) < 0) {
359 errstr(err, sizeof err);
360 reply(&r->work, &thdr, err);
361 freefid(r->work.fid);
362 r->busy = 0;
363 return;
364 }
365
366 f->f->inval = 1;
367 if(f->fid >= 0)
368 close(f->fid);
369 freefid(r->work.fid);
370
371 reply(&r->work, &thdr, 0);
372 r->busy = 0;
373
374 update(&stats->rpc[Tremove], t);
375 }
376
377 void
Xwstat(Fsrpc * r)378 Xwstat(Fsrpc *r)
379 {
380 char err[ERRMAX], path[128];
381 Fcall thdr;
382 Fid *f;
383 int s;
384 vlong t;
385
386 t = nsec();
387
388 f = getfid(r->work.fid);
389 if(f == 0) {
390 reply(&r->work, &thdr, Ebadfid);
391 r->busy = 0;
392 return;
393 }
394 if(f->fid >= 0)
395 s = fwstat(f->fid, r->work.stat, r->work.nstat);
396 else {
397 makepath(path, f->f, "");
398 s = wstat(path, r->work.stat, r->work.nstat);
399 }
400 if(s < 0) {
401 errstr(err, sizeof err);
402 reply(&r->work, &thdr, err);
403 }
404 else
405 reply(&r->work, &thdr, 0);
406
407 r->busy = 0;
408 update(&stats->rpc[Twstat], t);
409 }
410
411 void
slave(Fsrpc * f)412 slave(Fsrpc *f)
413 {
414 int r;
415 Proc *p;
416 uintptr pid;
417 static int nproc;
418
419 for(;;) {
420 for(p = Proclist; p; p = p->next) {
421 if(p->busy == 0) {
422 f->pid = p->pid;
423 p->busy = 1;
424 pid = (uintptr)rendezvous((void*)p->pid, f);
425 if(pid != p->pid)
426 fatal("rendezvous sync fail");
427 return;
428 }
429 }
430
431 if(++nproc > MAXPROC)
432 fatal("too many procs");
433
434 r = rfork(RFPROC|RFMEM);
435 if(r < 0)
436 fatal("rfork");
437
438 if(r == 0)
439 blockingslave();
440
441 p = malloc(sizeof(Proc));
442 if(p == 0)
443 fatal("out of memory");
444
445 p->busy = 0;
446 p->pid = r;
447 p->next = Proclist;
448 Proclist = p;
449
450 rendezvous((void*)p->pid, p);
451 }
452 }
453
454 void
blockingslave(void)455 blockingslave(void)
456 {
457 Proc *m;
458 uintptr pid;
459 Fsrpc *p;
460 Fcall thdr;
461
462 notify(flushaction);
463
464 pid = getpid();
465
466 m = rendezvous((void*)pid, 0);
467
468 for(;;) {
469 p = rendezvous((void*)pid, (void*)pid);
470 if(p == (void*)~0) /* Interrupted */
471 continue;
472
473 DEBUG(2, "\tslave: %p %F b %d p %p\n", pid, &p->work, p->busy, p->pid);
474 if(p->flushtag != NOTAG)
475 return;
476
477 switch(p->work.type) {
478 case Tread:
479 slaveread(p);
480 break;
481 case Twrite:
482 slavewrite(p);
483 break;
484 case Topen:
485 slaveopen(p);
486 break;
487 default:
488 reply(&p->work, &thdr, "exportfs: slave type error");
489 }
490 if(p->flushtag != NOTAG) {
491 p->work.type = Tflush;
492 p->work.tag = p->flushtag;
493 reply(&p->work, &thdr, 0);
494 }
495 p->busy = 0;
496 m->busy = 0;
497 }
498 }
499
500 void
slaveopen(Fsrpc * p)501 slaveopen(Fsrpc *p)
502 {
503 char err[ERRMAX], path[128];
504 Fcall *work, thdr;
505 Fid *f;
506 vlong t;
507
508 work = &p->work;
509
510 t = nsec();
511
512 f = getfid(work->fid);
513 if(f == 0) {
514 reply(work, &thdr, Ebadfid);
515 return;
516 }
517 if(f->fid >= 0) {
518 close(f->fid);
519 f->fid = -1;
520 }
521
522 makepath(path, f->f, "");
523 DEBUG(2, "\topen: %s %d\n", path, work->mode);
524
525 p->canint = 1;
526 if(p->flushtag != NOTAG)
527 return;
528
529 if(!okfile(path, work->mode)){
530 snprint(err, sizeof err, "iostats can't simulate %s", path);
531 reply(work, &thdr, err);
532 return;
533 }
534
535 /* There is a race here I ignore because there are no locks */
536 f->fid = open(path, work->mode);
537 p->canint = 0;
538 if(f->fid < 0) {
539 errstr(err, sizeof err);
540 reply(work, &thdr, err);
541 return;
542 }
543
544 DEBUG(2, "\topen: fd %d\n", f->fid);
545 f->mode = work->mode;
546 thdr.iounit = myiounit;
547 thdr.qid = f->f->qid;
548 reply(work, &thdr, 0);
549
550 update(&stats->rpc[Topen], t);
551 }
552
553 void
slaveread(Fsrpc * p)554 slaveread(Fsrpc *p)
555 {
556 char data[Maxfdata], err[ERRMAX];
557 Fcall *work, thdr;
558 Fid *f;
559 int n, r;
560 vlong t;
561
562 work = &p->work;
563
564 t = nsec();
565
566 f = getfid(work->fid);
567 if(f == 0) {
568 reply(work, &thdr, Ebadfid);
569 return;
570 }
571
572 n = (work->count > Maxfdata) ? Maxfdata : work->count;
573 p->canint = 1;
574 if(p->flushtag != NOTAG)
575 return;
576 /* can't just call pread, since directories must update the offset */
577 if(f->f->qid.type&QTDIR){
578 if(work->offset != f->offset){
579 if(work->offset != 0){
580 snprint(err, sizeof err, "can't seek in directory from %lld to %lld", f->offset, work->offset);
581 reply(work, &thdr, err);
582 return;
583 }
584 if(seek(f->fid, 0, 0) != 0){
585 errstr(err, sizeof err);
586 reply(work, &thdr, err);
587 return;
588 }
589 f->offset = 0;
590 }
591 r = read(f->fid, data, n);
592 if(r > 0)
593 f->offset += r;
594 }else
595 r = pread(f->fid, data, n, work->offset);
596 p->canint = 0;
597 if(r < 0) {
598 errstr(err, sizeof err);
599 reply(work, &thdr, err);
600 return;
601 }
602
603 DEBUG(2, "\tread: fd=%d %d bytes\n", f->fid, r);
604
605 thdr.data = data;
606 thdr.count = r;
607 stats->totread += r;
608 f->nread++;
609 f->bread += r;
610 reply(work, &thdr, 0);
611
612 update(&stats->rpc[Tread], t);
613 }
614
615 void
slavewrite(Fsrpc * p)616 slavewrite(Fsrpc *p)
617 {
618 char err[ERRMAX];
619 Fcall *work, thdr;
620 Fid *f;
621 int n;
622 vlong t;
623
624 work = &p->work;
625
626 t = nsec();
627
628 f = getfid(work->fid);
629 if(f == 0) {
630 reply(work, &thdr, Ebadfid);
631 return;
632 }
633
634 n = (work->count > Maxfdata) ? Maxfdata : work->count;
635 p->canint = 1;
636 if(p->flushtag != NOTAG)
637 return;
638 n = pwrite(f->fid, work->data, n, work->offset);
639 p->canint = 0;
640 if(n < 0) {
641 errstr(err, sizeof err);
642 reply(work, &thdr, err);
643 return;
644 }
645
646 DEBUG(2, "\twrite: %d bytes fd=%d\n", n, f->fid);
647
648 thdr.count = n;
649 f->nwrite++;
650 f->bwrite += n;
651 stats->totwrite += n;
652 reply(work, &thdr, 0);
653
654 update(&stats->rpc[Twrite], t);
655 }
656
657 void
reopen(Fid * f)658 reopen(Fid *f)
659 {
660 USED(f);
661 fatal("reopen");
662 }
663
664 void
flushaction(void * a,char * cause)665 flushaction(void *a, char *cause)
666 {
667 USED(a);
668 if(strncmp(cause, "kill", 4) == 0)
669 noted(NDFLT);
670
671 noted(NCONT);
672 }
673