fsys.c (18312B)
1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <regexp.h> 5 #include <thread.h> 6 #include <fcall.h> 7 #include <plumb.h> 8 #include "plumber.h" 9 10 enum 11 { 12 Stack = 32*1024 13 }; 14 15 typedef struct Dirtab Dirtab; 16 typedef struct Fid Fid; 17 typedef struct Holdq Holdq; 18 typedef struct Readreq Readreq; 19 typedef struct Sendreq Sendreq; 20 21 struct Dirtab 22 { 23 char *name; 24 uchar type; 25 uint qid; 26 uint perm; 27 int nopen; /* #fids open on this port */ 28 Fid *fopen; 29 Holdq *holdq; 30 Readreq *readq; 31 Sendreq *sendq; 32 }; 33 34 struct Fid 35 { 36 int fid; 37 int busy; 38 int open; 39 int mode; 40 Qid qid; 41 Dirtab *dir; 42 long offset; /* zeroed at beginning of each message, read or write */ 43 char *writebuf; /* partial message written so far; offset tells how much */ 44 Fid *next; 45 Fid *nextopen; 46 }; 47 48 struct Readreq 49 { 50 Fid *fid; 51 Fcall *fcall; 52 uchar *buf; 53 Readreq *next; 54 }; 55 56 struct Sendreq 57 { 58 int nfid; /* number of fids that should receive this message */ 59 int nleft; /* number left that haven't received it */ 60 Fid **fid; /* fid[nfid] */ 61 Plumbmsg *msg; 62 char *pack; /* plumbpack()ed message */ 63 int npack; /* length of pack */ 64 Sendreq *next; 65 }; 66 67 struct Holdq 68 { 69 Plumbmsg *msg; 70 Holdq *next; 71 }; 72 73 struct /* needed because incref() doesn't return value */ 74 { 75 Lock lk; 76 int ref; 77 } rulesref; 78 79 enum 80 { 81 NDIR = 50, 82 Nhash = 16, 83 84 Qdir = 0, 85 Qrules = 1, 86 Qsend = 2, 87 Qport = 3, 88 NQID = Qport 89 }; 90 91 static Dirtab dir[NDIR] = 92 { 93 { ".", QTDIR, Qdir, 0500|DMDIR }, 94 { "rules", QTFILE, Qrules, 0600 }, 95 { "send", QTFILE, Qsend, 0200 } 96 }; 97 static int ndir = NQID; 98 99 static int srvfd; 100 #define clock plumbclock /* SunOS name clash */ 101 static int clock; 102 static Fid *fids[Nhash]; 103 static QLock readlock; 104 static QLock queue; 105 static int messagesize = 8192+IOHDRSZ; /* good start */ 106 107 static void fsysproc(void*); 108 static void fsysrespond(Fcall*, uchar*, char*); 109 static Fid* newfid(int); 110 111 static Fcall* fsysflush(Fcall*, uchar*, Fid*); 112 static Fcall* fsysversion(Fcall*, uchar*, Fid*); 113 static Fcall* fsysauth(Fcall*, uchar*, Fid*); 114 static Fcall* fsysattach(Fcall*, uchar*, Fid*); 115 static Fcall* fsyswalk(Fcall*, uchar*, Fid*); 116 static Fcall* fsysopen(Fcall*, uchar*, Fid*); 117 static Fcall* fsyscreate(Fcall*, uchar*, Fid*); 118 static Fcall* fsysread(Fcall*, uchar*, Fid*); 119 static Fcall* fsyswrite(Fcall*, uchar*, Fid*); 120 static Fcall* fsysclunk(Fcall*, uchar*, Fid*); 121 static Fcall* fsysremove(Fcall*, uchar*, Fid*); 122 static Fcall* fsysstat(Fcall*, uchar*, Fid*); 123 static Fcall* fsyswstat(Fcall*, uchar*, Fid*); 124 125 Fcall* (*fcall[Tmax])(Fcall*, uchar*, Fid*); 126 127 static void 128 initfcall(void) 129 { 130 fcall[Tflush] = fsysflush; 131 fcall[Tversion] = fsysversion; 132 fcall[Tauth] = fsysauth; 133 fcall[Tattach] = fsysattach; 134 fcall[Twalk] = fsyswalk; 135 fcall[Topen] = fsysopen; 136 fcall[Tcreate] = fsyscreate; 137 fcall[Tread] = fsysread; 138 fcall[Twrite] = fsyswrite; 139 fcall[Tclunk] = fsysclunk; 140 fcall[Tremove]= fsysremove; 141 fcall[Tstat] = fsysstat; 142 fcall[Twstat] = fsyswstat; 143 } 144 145 char Ebadfcall[] = "bad fcall type"; 146 char Eperm[] = "permission denied"; 147 char Enomem[] = "malloc failed for buffer"; 148 char Enotdir[] = "not a directory"; 149 char Enoexist[] = "plumb file does not exist"; 150 char Eisdir[] = "file is a directory"; 151 char Ebadmsg[] = "bad plumb message format"; 152 char Enosuchport[] ="no such plumb port"; 153 char Enoport[] = "couldn't find destination for message"; 154 char Einuse[] = "file already open"; 155 156 /* 157 * Add new port. A no-op if port already exists or is the null string 158 */ 159 void 160 addport(char *port) 161 { 162 int i; 163 164 if(port == nil) 165 return; 166 for(i=NQID; i<ndir; i++) 167 if(strcmp(port, dir[i].name) == 0) 168 return; 169 if(i == NDIR){ 170 fprint(2, "plumb: too many ports; max %d\n", NDIR); 171 return; 172 } 173 ndir++; 174 dir[i].name = estrdup(port); 175 dir[i].qid = i; 176 dir[i].perm = 0400; 177 nports++; 178 ports = erealloc(ports, nports*sizeof(char*)); 179 ports[nports-1] = dir[i].name; 180 } 181 182 static ulong 183 getclock(void) 184 { 185 return time(0); 186 } 187 188 void 189 startfsys(int foreground) 190 { 191 int p[2]; 192 193 fmtinstall('F', fcallfmt); 194 clock = getclock(); 195 if(pipe(p) < 0) 196 error("can't create pipe: %r"); 197 /* 0 will be server end, 1 will be client end */ 198 srvfd = p[0]; 199 if(post9pservice(p[1], "plumb", nil) < 0) 200 sysfatal("post9pservice plumb: %r"); 201 close(p[1]); 202 if(foreground) 203 fsysproc(nil); 204 else 205 proccreate(fsysproc, nil, Stack); 206 } 207 208 static void 209 fsysproc(void *v) 210 { 211 int n; 212 Fcall *t; 213 Fid *f; 214 uchar *buf; 215 216 USED(v); 217 initfcall(); 218 t = nil; 219 for(;;){ 220 buf = malloc(messagesize); /* avoid memset of emalloc */ 221 if(buf == nil) 222 error("malloc failed: %r"); 223 qlock(&readlock); 224 n = read9pmsg(srvfd, buf, messagesize); 225 if(n <= 0){ 226 if(n < 0) 227 error("i/o error on server channel"); 228 threadexitsall("unmounted"); 229 } 230 /* 231 * can give false positive (create an extra fsysproc) once in a while, 232 * but no false negatives, so good enough. once we have one extra 233 * we'll never have more. 234 */ 235 if(readlock.waiting.head == nil) /* no other processes waiting to read; start one */ 236 proccreate(fsysproc, nil, Stack); 237 qunlock(&readlock); 238 if(t == nil) 239 t = emalloc(sizeof(Fcall)); 240 if(convM2S(buf, n, t) != n) 241 error("convert error in convM2S"); 242 if(debug) 243 fprint(2, "<= %F\n", t); 244 if(fcall[t->type] == 0) 245 fsysrespond(t, buf, Ebadfcall); 246 else{ 247 if(t->type==Tversion || t->type==Tauth) 248 f = nil; 249 else 250 f = newfid(t->fid); 251 t = (*fcall[t->type])(t, buf, f); 252 } 253 } 254 } 255 256 static void 257 fsysrespond(Fcall *t, uchar *buf, char *err) 258 { 259 int n; 260 261 if(err){ 262 t->type = Rerror; 263 t->ename = err; 264 }else 265 t->type++; 266 if(buf == nil) 267 buf = emalloc(messagesize); 268 n = convS2M(t, buf, messagesize); 269 if(n < 0) 270 error("convert error in convS2M"); 271 if(write(srvfd, buf, n) != n) 272 error("write error in respond"); 273 if(debug) 274 fprint(2, "=> %F\n", t); 275 free(buf); 276 } 277 278 static 279 Fid* 280 newfid(int fid) 281 { 282 Fid *f, *ff, **fh; 283 284 qlock(&queue); 285 ff = nil; 286 fh = &fids[fid&(Nhash-1)]; 287 for(f=*fh; f; f=f->next) 288 if(f->fid == fid) 289 goto Return; 290 else if(ff==nil && !f->busy) 291 ff = f; 292 if(ff){ 293 ff->fid = fid; 294 f = ff; 295 goto Return; 296 } 297 f = emalloc(sizeof *f); 298 f->fid = fid; 299 f->next = *fh; 300 *fh = f; 301 Return: 302 qunlock(&queue); 303 return f; 304 } 305 306 static uint 307 dostat(Dirtab *dir, uchar *buf, uint nbuf, uint clock) 308 { 309 Dir d; 310 311 d.qid.type = dir->type; 312 d.qid.path = dir->qid; 313 d.qid.vers = 0; 314 d.mode = dir->perm; 315 d.length = 0; /* would be nice to do better */ 316 d.name = dir->name; 317 d.uid = user; 318 d.gid = user; 319 d.muid = user; 320 d.atime = clock; 321 d.mtime = clock; 322 return convD2M(&d, buf, nbuf); 323 } 324 325 static void 326 queuesend(Dirtab *d, Plumbmsg *m) 327 { 328 Sendreq *s, *t; 329 Fid *f; 330 int i; 331 332 s = emalloc(sizeof(Sendreq)); 333 s->nfid = d->nopen; 334 s->nleft = s->nfid; 335 s->fid = emalloc(s->nfid*sizeof(Fid*)); 336 i = 0; 337 /* build array of fids open on this channel */ 338 for(f=d->fopen; f!=nil; f=f->nextopen) 339 s->fid[i++] = f; 340 s->msg = m; 341 s->next = nil; 342 /* link to end of queue; drainqueue() searches in sender order so this implements a FIFO */ 343 for(t=d->sendq; t!=nil; t=t->next) 344 if(t->next == nil) 345 break; 346 if(t == nil) 347 d->sendq = s; 348 else 349 t->next = s; 350 } 351 352 static void 353 queueread(Dirtab *d, Fcall *t, uchar *buf, Fid *f) 354 { 355 Readreq *r; 356 357 r = emalloc(sizeof(Readreq)); 358 r->fcall = t; 359 r->buf = buf; 360 r->fid = f; 361 r->next = d->readq; 362 d->readq = r; 363 } 364 365 static void 366 drainqueue(Dirtab *d) 367 { 368 Readreq *r, *nextr, *prevr; 369 Sendreq *s, *nexts, *prevs; 370 int i, n; 371 372 prevs = nil; 373 for(s=d->sendq; s!=nil; s=nexts){ 374 nexts = s->next; 375 for(i=0; i<s->nfid; i++){ 376 prevr = nil; 377 for(r=d->readq; r!=nil; r=nextr){ 378 nextr = r->next; 379 if(r->fid == s->fid[i]){ 380 /* pack the message if necessary */ 381 if(s->pack == nil) 382 s->pack = plumbpack(s->msg, &s->npack); 383 /* exchange the stuff... */ 384 r->fcall->data = s->pack+r->fid->offset; 385 n = s->npack - r->fid->offset; 386 if(n > messagesize-IOHDRSZ) 387 n = messagesize-IOHDRSZ; 388 if(n > r->fcall->count) 389 n = r->fcall->count; 390 r->fcall->count = n; 391 fsysrespond(r->fcall, r->buf, nil); 392 r->fid->offset += n; 393 if(r->fid->offset >= s->npack){ 394 /* message transferred; delete this fid from send queue */ 395 r->fid->offset = 0; 396 s->fid[i] = nil; 397 s->nleft--; 398 } 399 /* delete read request from queue */ 400 if(prevr) 401 prevr->next = r->next; 402 else 403 d->readq = r->next; 404 free(r->fcall); 405 free(r); 406 break; 407 }else 408 prevr = r; 409 } 410 } 411 /* if no fids left, delete this send from queue */ 412 if(s->nleft == 0){ 413 free(s->fid); 414 plumbfree(s->msg); 415 free(s->pack); 416 if(prevs) 417 prevs->next = s->next; 418 else 419 d->sendq = s->next; 420 free(s); 421 }else 422 prevs = s; 423 } 424 } 425 426 /* can't flush a send because they are always answered synchronously */ 427 static void 428 flushqueue(Dirtab *d, int oldtag) 429 { 430 Readreq *r, *prevr; 431 432 prevr = nil; 433 for(r=d->readq; r!=nil; r=r->next){ 434 if(oldtag == r->fcall->tag){ 435 /* delete read request from queue */ 436 if(prevr) 437 prevr->next = r->next; 438 else 439 d->readq = r->next; 440 free(r->fcall); 441 free(r->buf); 442 free(r); 443 return; 444 } 445 prevr = r; 446 } 447 } 448 449 /* remove messages awaiting delivery to now-closing fid */ 450 static void 451 removesenders(Dirtab *d, Fid *fid) 452 { 453 Sendreq *s, *nexts, *prevs; 454 int i; 455 456 prevs = nil; 457 for(s=d->sendq; s!=nil; s=nexts){ 458 nexts = s->next; 459 for(i=0; i<s->nfid; i++) 460 if(fid == s->fid[i]){ 461 /* delete this fid from send queue */ 462 s->fid[i] = nil; 463 s->nleft--; 464 break; 465 } 466 /* if no fids left, delete this send from queue */ 467 if(s->nleft == 0){ 468 free(s->fid); 469 plumbfree(s->msg); 470 free(s->pack); 471 if(prevs) 472 prevs->next = s->next; 473 else 474 d->sendq = s->next; 475 free(s); 476 }else 477 prevs = s; 478 } 479 } 480 481 static void 482 hold(Plumbmsg *m, Dirtab *d) 483 { 484 Holdq *h, *q; 485 486 h = emalloc(sizeof(Holdq)); 487 h->msg = m; 488 /* add to end of queue */ 489 if(d->holdq == nil) 490 d->holdq = h; 491 else{ 492 for(q=d->holdq; q->next!=nil; q=q->next) 493 ; 494 q->next = h; 495 } 496 } 497 498 static void 499 queueheld(Dirtab *d) 500 { 501 Holdq *h; 502 503 while(d->holdq != nil){ 504 h = d->holdq; 505 d->holdq = h->next; 506 queuesend(d, h->msg); 507 /* no need to drain queue because we know no-one is reading yet */ 508 free(h); 509 } 510 } 511 512 static void 513 dispose(Fcall *t, uchar *buf, Plumbmsg *m, Ruleset *rs, Exec *e) 514 { 515 int i; 516 char *err; 517 518 qlock(&queue); 519 err = nil; 520 if(m->dst==nil || m->dst[0]=='\0'){ 521 err = Enoport; 522 if(rs != nil) 523 err = startup(rs, e); 524 plumbfree(m); 525 }else 526 for(i=NQID; i<ndir; i++) 527 if(strcmp(m->dst, dir[i].name) == 0){ 528 if(dir[i].nopen == 0){ 529 err = startup(rs, e); 530 if(e!=nil && e->holdforclient) 531 hold(m, &dir[i]); 532 else 533 plumbfree(m); 534 }else{ 535 queuesend(&dir[i], m); 536 drainqueue(&dir[i]); 537 } 538 break; 539 } 540 freeexec(e); 541 qunlock(&queue); 542 fsysrespond(t, buf, err); 543 free(t); 544 } 545 546 static Fcall* 547 fsysversion(Fcall *t, uchar *buf, Fid *fid) 548 { 549 USED(fid); 550 551 if(t->msize < 256){ 552 fsysrespond(t, buf, "version: message size too small"); 553 return t; 554 } 555 if(t->msize < messagesize) 556 messagesize = t->msize; 557 t->msize = messagesize; 558 if(strncmp(t->version, "9P2000", 6) != 0){ 559 fsysrespond(t, buf, "unrecognized 9P version"); 560 return t; 561 } 562 t->version = "9P2000"; 563 fsysrespond(t, buf, nil); 564 return t; 565 } 566 567 static Fcall* 568 fsysauth(Fcall *t, uchar *buf, Fid *fid) 569 { 570 USED(fid); 571 fsysrespond(t, buf, "plumber: authentication not required"); 572 return t; 573 } 574 575 static Fcall* 576 fsysattach(Fcall *t, uchar *buf, Fid *f) 577 { 578 Fcall out; 579 580 /* 581 if(strcmp(t->uname, user) != 0){ 582 fsysrespond(&out, buf, Eperm); 583 return t; 584 } 585 */ 586 f->busy = 1; 587 f->open = 0; 588 f->qid.type = QTDIR; 589 f->qid.path = Qdir; 590 f->qid.vers = 0; 591 f->dir = dir; 592 memset(&out, 0, sizeof(Fcall)); 593 out.type = t->type; 594 out.tag = t->tag; 595 out.fid = f->fid; 596 out.qid = f->qid; 597 fsysrespond(&out, buf, nil); 598 return t; 599 } 600 601 static Fcall* 602 fsysflush(Fcall *t, uchar *buf, Fid *fid) 603 { 604 int i; 605 606 USED(fid); 607 qlock(&queue); 608 for(i=NQID; i<ndir; i++) 609 flushqueue(&dir[i], t->oldtag); 610 qunlock(&queue); 611 fsysrespond(t, buf, nil); 612 return t; 613 } 614 615 static Fcall* 616 fsyswalk(Fcall *t, uchar *buf, Fid *f) 617 { 618 Fcall out; 619 Fid *nf; 620 ulong path; 621 Dirtab *d, *dir; 622 Qid q; 623 int i; 624 uchar type; 625 char *err; 626 627 if(f->open){ 628 fsysrespond(t, buf, "clone of an open fid"); 629 return t; 630 } 631 632 nf = nil; 633 if(t->fid != t->newfid){ 634 nf = newfid(t->newfid); 635 if(nf->busy){ 636 fsysrespond(t, buf, "clone to a busy fid"); 637 return t; 638 } 639 nf->busy = 1; 640 nf->open = 0; 641 nf->dir = f->dir; 642 nf->qid = f->qid; 643 f = nf; /* walk f */ 644 } 645 646 out.nwqid = 0; 647 err = nil; 648 dir = f->dir; 649 q = f->qid; 650 651 if(t->nwname > 0){ 652 for(i=0; i<t->nwname; i++){ 653 if((q.type & QTDIR) == 0){ 654 err = Enotdir; 655 break; 656 } 657 if(strcmp(t->wname[i], "..") == 0){ 658 type = QTDIR; 659 path = Qdir; 660 Accept: 661 q.type = type; 662 q.vers = 0; 663 q.path = path; 664 out.wqid[out.nwqid++] = q; 665 continue; 666 } 667 d = dir; 668 d++; /* skip '.' */ 669 for(; d->name; d++) 670 if(strcmp(t->wname[i], d->name) == 0){ 671 type = d->type; 672 path = d->qid; 673 dir = d; 674 goto Accept; 675 } 676 err = Enoexist; 677 break; 678 } 679 } 680 681 out.type = t->type; 682 out.tag = t->tag; 683 if(err!=nil || out.nwqid<t->nwname){ 684 if(nf) 685 nf->busy = 0; 686 }else if(out.nwqid == t->nwname){ 687 f->qid = q; 688 f->dir = dir; 689 } 690 691 fsysrespond(&out, buf, err); 692 return t; 693 } 694 695 static Fcall* 696 fsysopen(Fcall *t, uchar *buf, Fid *f) 697 { 698 int m, clearrules, mode; 699 700 clearrules = 0; 701 if(t->mode & OTRUNC){ 702 if(f->qid.path != Qrules) 703 goto Deny; 704 clearrules = 1; 705 } 706 /* can't truncate anything, so just disregard */ 707 mode = t->mode & ~(OTRUNC|OCEXEC); 708 /* can't execute or remove anything */ 709 if(mode==OEXEC || (mode&ORCLOSE)) 710 goto Deny; 711 switch(mode){ 712 default: 713 goto Deny; 714 case OREAD: 715 m = 0400; 716 break; 717 case OWRITE: 718 m = 0200; 719 break; 720 case ORDWR: 721 m = 0600; 722 break; 723 } 724 if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m) 725 goto Deny; 726 if(f->qid.path==Qrules && (mode==OWRITE || mode==ORDWR)){ 727 lock(&rulesref.lk); 728 if(rulesref.ref++ != 0){ 729 rulesref.ref--; 730 unlock(&rulesref.lk); 731 fsysrespond(t, buf, Einuse); 732 return t; 733 } 734 unlock(&rulesref.lk); 735 } 736 if(clearrules){ 737 writerules(nil, 0); 738 rules[0] = nil; 739 } 740 t->qid = f->qid; 741 t->iounit = 0; 742 qlock(&queue); 743 f->mode = mode; 744 f->open = 1; 745 f->dir->nopen++; 746 f->nextopen = f->dir->fopen; 747 f->dir->fopen = f; 748 queueheld(f->dir); 749 qunlock(&queue); 750 fsysrespond(t, buf, nil); 751 return t; 752 753 Deny: 754 fsysrespond(t, buf, Eperm); 755 return t; 756 } 757 758 static Fcall* 759 fsyscreate(Fcall *t, uchar *buf, Fid *fid) 760 { 761 USED(fid); 762 fsysrespond(t, buf, Eperm); 763 return t; 764 } 765 766 static Fcall* 767 fsysreadrules(Fcall *t, uchar *buf) 768 { 769 char *p; 770 int n; 771 772 p = printrules(); 773 n = strlen(p); 774 t->data = p; 775 if(t->offset >= n) 776 t->count = 0; 777 else{ 778 t->data = p+t->offset; 779 if(t->offset+t->count > n) 780 t->count = n-t->offset; 781 } 782 fsysrespond(t, buf, nil); 783 free(p); 784 return t; 785 } 786 787 static Fcall* 788 fsysread(Fcall *t, uchar *buf, Fid *f) 789 { 790 uchar *b; 791 int i, n, o, e; 792 uint len; 793 Dirtab *d; 794 uint clock; 795 796 if(f->qid.path != Qdir){ 797 if(f->qid.path == Qrules) 798 return fsysreadrules(t, buf); 799 /* read from port */ 800 if(f->qid.path < NQID){ 801 fsysrespond(t, buf, "internal error: unknown read port"); 802 return t; 803 } 804 qlock(&queue); 805 queueread(f->dir, t, buf, f); 806 drainqueue(f->dir); 807 qunlock(&queue); 808 return nil; 809 } 810 o = t->offset; 811 e = t->offset+t->count; 812 clock = getclock(); 813 b = malloc(messagesize-IOHDRSZ); 814 if(b == nil){ 815 fsysrespond(t, buf, Enomem); 816 return t; 817 } 818 n = 0; 819 d = dir; 820 d++; /* first entry is '.' */ 821 for(i=0; d->name!=nil && i<e; i+=len){ 822 len = dostat(d, b+n, messagesize-IOHDRSZ-n, clock); 823 if(len <= BIT16SZ) 824 break; 825 if(i >= o) 826 n += len; 827 d++; 828 } 829 t->data = (char*)b; 830 t->count = n; 831 fsysrespond(t, buf, nil); 832 free(b); 833 return t; 834 } 835 836 static Fcall* 837 fsyswrite(Fcall *t, uchar *buf, Fid *f) 838 { 839 Plumbmsg *m; 840 int i, n; 841 long count; 842 char *data; 843 Exec *e; 844 845 switch((int)f->qid.path){ 846 case Qdir: 847 fsysrespond(t, buf, Eisdir); 848 return t; 849 case Qrules: 850 clock = getclock(); 851 fsysrespond(t, buf, writerules(t->data, t->count)); 852 return t; 853 case Qsend: 854 if(f->offset == 0){ 855 data = t->data; 856 count = t->count; 857 }else{ 858 /* partial message already assembled */ 859 f->writebuf = erealloc(f->writebuf, f->offset + t->count); 860 memmove(f->writebuf+f->offset, t->data, t->count); 861 data = f->writebuf; 862 count = f->offset+t->count; 863 } 864 m = plumbunpackpartial(data, count, &n); 865 if(m == nil){ 866 if(n == 0){ 867 f->offset = 0; 868 free(f->writebuf); 869 f->writebuf = nil; 870 fsysrespond(t, buf, Ebadmsg); 871 return t; 872 } 873 /* can read more... */ 874 if(f->offset == 0){ 875 f->writebuf = emalloc(t->count); 876 memmove(f->writebuf, t->data, t->count); 877 } 878 /* else buffer has already been grown */ 879 f->offset += t->count; 880 fsysrespond(t, buf, nil); 881 return t; 882 } 883 /* release partial buffer */ 884 f->offset = 0; 885 free(f->writebuf); 886 f->writebuf = nil; 887 for(i=0; rules[i]; i++) 888 if((e=matchruleset(m, rules[i])) != nil){ 889 dispose(t, buf, m, rules[i], e); 890 return nil; 891 } 892 if(m->dst != nil){ 893 dispose(t, buf, m, nil, nil); 894 return nil; 895 } 896 fsysrespond(t, buf, "no matching plumb rule"); 897 return t; 898 } 899 fsysrespond(t, buf, "internal error: write to unknown file"); 900 return t; 901 } 902 903 static Fcall* 904 fsysstat(Fcall *t, uchar *buf, Fid *f) 905 { 906 t->stat = emalloc(messagesize-IOHDRSZ); 907 t->nstat = dostat(f->dir, t->stat, messagesize-IOHDRSZ, clock); 908 fsysrespond(t, buf, nil); 909 free(t->stat); 910 t->stat = nil; 911 return t; 912 } 913 914 static Fcall* 915 fsyswstat(Fcall *t, uchar *buf, Fid *fid) 916 { 917 USED(fid); 918 fsysrespond(t, buf, Eperm); 919 return t; 920 } 921 922 static Fcall* 923 fsysremove(Fcall *t, uchar *buf, Fid *fid) 924 { 925 USED(fid); 926 fsysrespond(t, buf, Eperm); 927 return t; 928 } 929 930 static Fcall* 931 fsysclunk(Fcall *t, uchar *buf, Fid *f) 932 { 933 Fid *prev, *p; 934 Dirtab *d; 935 936 qlock(&queue); 937 if(f->open){ 938 d = f->dir; 939 d->nopen--; 940 if(d->qid==Qrules && (f->mode==OWRITE || f->mode==ORDWR)){ 941 /* 942 * just to be sure last rule is parsed; error messages will be lost, though, 943 * unless last write ended with a blank line 944 */ 945 writerules(nil, 0); 946 lock(&rulesref.lk); 947 rulesref.ref--; 948 unlock(&rulesref.lk); 949 } 950 prev = nil; 951 for(p=d->fopen; p; p=p->nextopen){ 952 if(p == f){ 953 if(prev) 954 prev->nextopen = f->nextopen; 955 else 956 d->fopen = f->nextopen; 957 removesenders(d, f); 958 break; 959 } 960 prev = p; 961 } 962 } 963 f->busy = 0; 964 f->open = 0; 965 f->offset = 0; 966 if(f->writebuf != nil){ 967 free(f->writebuf); 968 f->writebuf = nil; 969 } 970 qunlock(&queue); 971 fsysrespond(t, buf, nil); 972 return t; 973 }