1 #include <u.h> 2 #include <libc.h> 3 #include <draw.h> 4 #include <thread.h> 5 #include <cursor.h> 6 #include <mouse.h> 7 #include <keyboard.h> 8 #include <frame.h> 9 #include <fcall.h> 10 #include <plumb.h> 11 #include "dat.h" 12 #include "fns.h" 13 14 static int cfd; 15 static int sfd; 16 17 enum 18 { 19 Nhash = 16, 20 DEBUG = 0 21 }; 22 23 static Fid *fids[Nhash]; 24 25 Fid *newfid(int); 26 27 static Xfid* fsysflush(Xfid*, Fid*); 28 static Xfid* fsysauth(Xfid*, Fid*); 29 static Xfid* fsysversion(Xfid*, Fid*); 30 static Xfid* fsysattach(Xfid*, Fid*); 31 static Xfid* fsyswalk(Xfid*, Fid*); 32 static Xfid* fsysopen(Xfid*, Fid*); 33 static Xfid* fsyscreate(Xfid*, Fid*); 34 static Xfid* fsysread(Xfid*, Fid*); 35 static Xfid* fsyswrite(Xfid*, Fid*); 36 static Xfid* fsysclunk(Xfid*, Fid*); 37 static Xfid* fsysremove(Xfid*, Fid*); 38 static Xfid* fsysstat(Xfid*, Fid*); 39 static Xfid* fsyswstat(Xfid*, Fid*); 40 41 Xfid* (*fcall[Tmax])(Xfid*, Fid*) = 42 { 43 [Tflush] = fsysflush, 44 [Tversion] = fsysversion, 45 [Tauth] = fsysauth, 46 [Tattach] = fsysattach, 47 [Twalk] = fsyswalk, 48 [Topen] = fsysopen, 49 [Tcreate] = fsyscreate, 50 [Tread] = fsysread, 51 [Twrite] = fsyswrite, 52 [Tclunk] = fsysclunk, 53 [Tremove]= fsysremove, 54 [Tstat] = fsysstat, 55 [Twstat] = fsyswstat, 56 }; 57 58 char Eperm[] = "permission denied"; 59 char Eexist[] = "file does not exist"; 60 char Enotdir[] = "not a directory"; 61 62 Dirtab dirtab[]= 63 { 64 { ".", QTDIR, Qdir, 0500|DMDIR }, 65 { "acme", QTDIR, Qacme, 0500|DMDIR }, 66 { "cons", QTFILE, Qcons, 0600 }, 67 { "consctl", QTFILE, Qconsctl, 0000 }, 68 { "draw", QTDIR, Qdraw, 0000|DMDIR }, /* to suppress graphics progs started in acme */ 69 { "editout", QTFILE, Qeditout, 0200 }, 70 { "index", QTFILE, Qindex, 0400 }, 71 { "label", QTFILE, Qlabel, 0600 }, 72 { "new", QTDIR, Qnew, 0500|DMDIR }, 73 { nil, } 74 }; 75 76 Dirtab dirtabw[]= 77 { 78 { ".", QTDIR, Qdir, 0500|DMDIR }, 79 { "addr", QTFILE, QWaddr, 0600 }, 80 { "body", QTAPPEND, QWbody, 0600|DMAPPEND }, 81 { "ctl", QTFILE, QWctl, 0600 }, 82 { "data", QTFILE, QWdata, 0600 }, 83 { "editout", QTFILE, QWeditout, 0200 }, 84 { "errors", QTFILE, QWerrors, 0200 }, 85 { "event", QTFILE, QWevent, 0600 }, 86 { "rdsel", QTFILE, QWrdsel, 0400 }, 87 { "wrsel", QTFILE, QWwrsel, 0200 }, 88 { "tag", QTAPPEND, QWtag, 0600|DMAPPEND }, 89 { "xdata", QTFILE, QWxdata, 0600 }, 90 { nil, } 91 }; 92 93 typedef struct Mnt Mnt; 94 struct Mnt 95 { 96 QLock; 97 int id; 98 Mntdir *md; 99 }; 100 101 Mnt mnt; 102 103 Xfid* respond(Xfid*, Fcall*, char*); 104 int dostat(int, Dirtab*, uchar*, int, uint); 105 uint getclock(void); 106 107 char *user = "Wile E. Coyote"; 108 int clockfd; 109 static int closing = 0; 110 int messagesize = Maxblock+IOHDRSZ; /* good start */ 111 112 void fsysproc(void *); 113 114 void 115 fsysinit(void) 116 { 117 int p[2]; 118 int n, fd; 119 char buf[256]; 120 121 if(pipe(p) < 0) 122 error("can't create pipe"); 123 cfd = p[0]; 124 sfd = p[1]; 125 fmtinstall('F', fcallfmt); 126 clockfd = open("/dev/time", OREAD|OCEXEC); 127 fd = open("/dev/user", OREAD); 128 if(fd >= 0){ 129 n = read(fd, buf, sizeof buf-1); 130 if(n > 0){ 131 buf[n] = 0; 132 user = estrdup(buf); 133 } 134 close(fd); 135 } 136 proccreate(fsysproc, nil, STACK); 137 } 138 139 void 140 fsysproc(void *) 141 { 142 int n; 143 Xfid *x; 144 Fid *f; 145 Fcall t; 146 uchar *buf; 147 148 threadsetname("fsysproc"); 149 150 x = nil; 151 for(;;){ 152 buf = emalloc(messagesize+UTFmax); /* overflow for appending partial rune in xfidwrite */ 153 n = read9pmsg(sfd, buf, messagesize); 154 if(n <= 0){ 155 if(closing) 156 break; 157 error("i/o error on server channel"); 158 } 159 if(x == nil){ 160 sendp(cxfidalloc, nil); 161 x = recvp(cxfidalloc); 162 } 163 x->buf = buf; 164 if(convM2S(buf, n, x) != n) 165 error("convert error in convM2S"); 166 if(DEBUG) 167 fprint(2, "%F\n", &x->Fcall); 168 if(fcall[x->type] == nil) 169 x = respond(x, &t, "bad fcall type"); 170 else{ 171 switch(x->type){ 172 case Tversion: 173 case Tauth: 174 case Tflush: 175 f = nil; 176 break; 177 case Tattach: 178 f = newfid(x->fid); 179 break; 180 default: 181 f = newfid(x->fid); 182 if(!f->busy){ 183 x->f = f; 184 x = respond(x, &t, "fid not in use"); 185 continue; 186 } 187 break; 188 } 189 x->f = f; 190 x = (*fcall[x->type])(x, f); 191 } 192 } 193 } 194 195 Mntdir* 196 fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl) 197 { 198 Mntdir *m; 199 int id; 200 201 qlock(&mnt); 202 id = ++mnt.id; 203 m = emalloc(sizeof *m); 204 m->id = id; 205 m->dir = dir; 206 m->ref = 1; /* one for Command, one will be incremented in attach */ 207 m->ndir = ndir; 208 m->next = mnt.md; 209 m->incl = incl; 210 m->nincl = nincl; 211 mnt.md = m; 212 qunlock(&mnt); 213 return m; 214 } 215 216 void 217 fsysincid(Mntdir *m) 218 { 219 qlock(&mnt); 220 m->ref++; 221 qunlock(&mnt); 222 } 223 224 void 225 fsysdelid(Mntdir *idm) 226 { 227 Mntdir *m, *prev; 228 int i; 229 char buf[64]; 230 231 if(idm == nil) 232 return; 233 qlock(&mnt); 234 if(--idm->ref > 0){ 235 qunlock(&mnt); 236 return; 237 } 238 prev = nil; 239 for(m=mnt.md; m; m=m->next){ 240 if(m == idm){ 241 if(prev) 242 prev->next = m->next; 243 else 244 mnt.md = m->next; 245 for(i=0; i<m->nincl; i++) 246 free(m->incl[i]); 247 free(m->incl); 248 free(m->dir); 249 free(m); 250 qunlock(&mnt); 251 return; 252 } 253 prev = m; 254 } 255 qunlock(&mnt); 256 sprint(buf, "fsysdelid: can't find id %d\n", idm->id); 257 sendp(cerr, estrdup(buf)); 258 } 259 260 /* 261 * Called only in exec.c:/^run(), from a different FD group 262 */ 263 Mntdir* 264 fsysmount(Rune *dir, int ndir, Rune **incl, int nincl) 265 { 266 char buf[256]; 267 Mntdir *m; 268 269 /* close server side so don't hang if acme is half-exited */ 270 close(sfd); 271 m = fsysaddid(dir, ndir, incl, nincl); 272 sprint(buf, "%d", m->id); 273 if(mount(cfd, -1, "/mnt/acme", MREPL, buf) < 0){ 274 fsysdelid(m); 275 return nil; 276 } 277 close(cfd); 278 bind("/mnt/acme", "/mnt/wsys", MREPL); 279 if(bind("/mnt/acme", "/dev", MBEFORE) < 0){ 280 fsysdelid(m); 281 return nil; 282 } 283 return m; 284 } 285 286 void 287 fsysclose(void) 288 { 289 closing = 1; 290 close(cfd); 291 close(sfd); 292 } 293 294 Xfid* 295 respond(Xfid *x, Fcall *t, char *err) 296 { 297 int n; 298 299 if(err){ 300 t->type = Rerror; 301 t->ename = err; 302 }else 303 t->type = x->type+1; 304 t->fid = x->fid; 305 t->tag = x->tag; 306 if(x->buf == nil) 307 x->buf = emalloc(messagesize); 308 n = convS2M(t, x->buf, messagesize); 309 if(n <= 0) 310 error("convert error in convS2M"); 311 if(write(sfd, x->buf, n) != n) 312 error("write error in respond"); 313 free(x->buf); 314 x->buf = nil; 315 if(DEBUG) 316 fprint(2, "r: %F\n", t); 317 return x; 318 } 319 320 static 321 Xfid* 322 fsysversion(Xfid *x, Fid*) 323 { 324 Fcall t; 325 326 if(x->msize < 256) 327 return respond(x, &t, "version: message size too small"); 328 if(x->msize < messagesize) 329 messagesize = x->msize; 330 t.msize = messagesize; 331 if(strncmp(x->version, "9P2000", 6) != 0) 332 return respond(x, &t, "unrecognized 9P version"); 333 t.version = "9P2000"; 334 return respond(x, &t, nil); 335 } 336 337 static 338 Xfid* 339 fsysauth(Xfid *x, Fid*) 340 { 341 Fcall t; 342 343 return respond(x, &t, "acme: authentication not required"); 344 } 345 346 static 347 Xfid* 348 fsysflush(Xfid *x, Fid*) 349 { 350 sendp(x->c, xfidflush); 351 return nil; 352 } 353 354 static 355 Xfid* 356 fsysattach(Xfid *x, Fid *f) 357 { 358 Fcall t; 359 int id; 360 Mntdir *m; 361 362 if(strcmp(x->uname, user) != 0) 363 return respond(x, &t, Eperm); 364 f->busy = TRUE; 365 f->open = FALSE; 366 f->qid.path = Qdir; 367 f->qid.type = QTDIR; 368 f->qid.vers = 0; 369 f->dir = dirtab; 370 f->nrpart = 0; 371 f->w = nil; 372 t.qid = f->qid; 373 f->mntdir = nil; 374 id = atoi(x->aname); 375 qlock(&mnt); 376 for(m=mnt.md; m; m=m->next) 377 if(m->id == id){ 378 f->mntdir = m; 379 m->ref++; 380 break; 381 } 382 if(m == nil) 383 sendp(cerr, estrdup("unknown id in attach")); 384 qunlock(&mnt); 385 return respond(x, &t, nil); 386 } 387 388 static 389 Xfid* 390 fsyswalk(Xfid *x, Fid *f) 391 { 392 Fcall t; 393 int c, i, j, id; 394 Qid q; 395 uchar type; 396 ulong path; 397 Fid *nf; 398 Dirtab *d, *dir; 399 Window *w; 400 char *err; 401 402 nf = nil; 403 w = nil; 404 if(f->open) 405 return respond(x, &t, "walk of open file"); 406 if(x->fid != x->newfid){ 407 nf = newfid(x->newfid); 408 if(nf->busy) 409 return respond(x, &t, "newfid already in use"); 410 nf->busy = TRUE; 411 nf->open = FALSE; 412 nf->mntdir = f->mntdir; 413 if(f->mntdir) 414 f->mntdir->ref++; 415 nf->dir = f->dir; 416 nf->qid = f->qid; 417 nf->w = f->w; 418 nf->nrpart = 0; /* not open, so must be zero */ 419 if(nf->w) 420 incref(nf->w); 421 f = nf; /* walk f */ 422 } 423 424 t.nwqid = 0; 425 err = nil; 426 dir = nil; 427 id = WIN(f->qid); 428 q = f->qid; 429 430 if(x->nwname > 0){ 431 for(i=0; i<x->nwname; i++){ 432 if((q.type & QTDIR) == 0){ 433 err = Enotdir; 434 break; 435 } 436 437 if(strcmp(x->wname[i], "..") == 0){ 438 type = QTDIR; 439 path = Qdir; 440 id = 0; 441 if(w){ 442 winclose(w); 443 w = nil; 444 } 445 Accept: 446 if(i == MAXWELEM){ 447 err = "name too long"; 448 break; 449 } 450 q.type = type; 451 q.vers = 0; 452 q.path = QID(id, path); 453 t.wqid[t.nwqid++] = q; 454 continue; 455 } 456 457 /* is it a numeric name? */ 458 for(j=0; (c=x->wname[i][j]); j++) 459 if(c<'0' || '9'<c) 460 goto Regular; 461 /* yes: it's a directory */ 462 if(w) /* name has form 27/23; get out before losing w */ 463 break; 464 id = atoi(x->wname[i]); 465 qlock(&row); 466 w = lookid(id, FALSE); 467 if(w == nil){ 468 qunlock(&row); 469 break; 470 } 471 incref(w); /* we'll drop reference at end if there's an error */ 472 path = Qdir; 473 type = QTDIR; 474 qunlock(&row); 475 dir = dirtabw; 476 goto Accept; 477 478 Regular: 479 // if(FILE(f->qid) == Qacme) /* empty directory */ 480 // break; 481 if(strcmp(x->wname[i], "new") == 0){ 482 if(w) 483 error("w set in walk to new"); 484 sendp(cnewwindow, nil); /* signal newwindowthread */ 485 w = recvp(cnewwindow); /* receive new window */ 486 incref(w); 487 type = QTDIR; 488 path = QID(w->id, Qdir); 489 id = w->id; 490 dir = dirtabw; 491 goto Accept; 492 } 493 494 if(id == 0) 495 d = dirtab; 496 else 497 d = dirtabw; 498 d++; /* skip '.' */ 499 for(; d->name; d++) 500 if(strcmp(x->wname[i], d->name) == 0){ 501 path = d->qid; 502 type = d->type; 503 dir = d; 504 goto Accept; 505 } 506 507 break; /* file not found */ 508 } 509 510 if(i==0 && err == nil) 511 err = Eexist; 512 } 513 514 if(err!=nil || t.nwqid<x->nwname){ 515 if(nf){ 516 nf->busy = FALSE; 517 fsysdelid(nf->mntdir); 518 } 519 }else if(t.nwqid == x->nwname){ 520 if(w){ 521 f->w = w; 522 w = nil; /* don't drop the reference */ 523 } 524 if(dir) 525 f->dir = dir; 526 f->qid = q; 527 } 528 529 if(w != nil) 530 winclose(w); 531 532 return respond(x, &t, err); 533 } 534 535 static 536 Xfid* 537 fsysopen(Xfid *x, Fid *f) 538 { 539 Fcall t; 540 int m; 541 542 /* can't truncate anything, so just disregard */ 543 x->mode &= ~(OTRUNC|OCEXEC); 544 /* can't execute or remove anything */ 545 if(x->mode==OEXEC || (x->mode&ORCLOSE)) 546 goto Deny; 547 switch(x->mode){ 548 default: 549 goto Deny; 550 case OREAD: 551 m = 0400; 552 break; 553 case OWRITE: 554 m = 0200; 555 break; 556 case ORDWR: 557 m = 0600; 558 break; 559 } 560 if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m) 561 goto Deny; 562 563 sendp(x->c, xfidopen); 564 return nil; 565 566 Deny: 567 return respond(x, &t, Eperm); 568 } 569 570 static 571 Xfid* 572 fsyscreate(Xfid *x, Fid*) 573 { 574 Fcall t; 575 576 return respond(x, &t, Eperm); 577 } 578 579 static 580 int 581 idcmp(void *a, void *b) 582 { 583 return *(int*)a - *(int*)b; 584 } 585 586 static 587 Xfid* 588 fsysread(Xfid *x, Fid *f) 589 { 590 Fcall t; 591 uchar *b; 592 int i, id, n, o, e, j, k, *ids, nids; 593 Dirtab *d, dt; 594 Column *c; 595 uint clock, len; 596 char buf[16]; 597 598 if(f->qid.type & QTDIR){ 599 if(FILE(f->qid) == Qacme){ /* empty dir */ 600 t.data = nil; 601 t.count = 0; 602 respond(x, &t, nil); 603 return x; 604 } 605 o = x->offset; 606 e = x->offset+x->count; 607 clock = getclock(); 608 b = emalloc(messagesize); 609 id = WIN(f->qid); 610 n = 0; 611 if(id > 0) 612 d = dirtabw; 613 else 614 d = dirtab; 615 d++; /* first entry is '.' */ 616 for(i=0; d->name!=nil && i<e; i+=len){ 617 len = dostat(WIN(x->f->qid), d, b+n, x->count-n, clock); 618 if(len <= BIT16SZ) 619 break; 620 if(i >= o) 621 n += len; 622 d++; 623 } 624 if(id == 0){ 625 qlock(&row); 626 nids = 0; 627 ids = nil; 628 for(j=0; j<row.ncol; j++){ 629 c = row.col[j]; 630 for(k=0; k<c->nw; k++){ 631 ids = erealloc(ids, (nids+1)*sizeof(int)); 632 ids[nids++] = c->w[k]->id; 633 } 634 } 635 qunlock(&row); 636 qsort(ids, nids, sizeof ids[0], idcmp); 637 j = 0; 638 dt.name = buf; 639 for(; j<nids && i<e; i+=len){ 640 k = ids[j]; 641 sprint(dt.name, "%d", k); 642 dt.qid = QID(k, Qdir); 643 dt.type = QTDIR; 644 dt.perm = DMDIR|0700; 645 len = dostat(k, &dt, b+n, x->count-n, clock); 646 if(len == 0) 647 break; 648 if(i >= o) 649 n += len; 650 j++; 651 } 652 free(ids); 653 } 654 t.data = (char*)b; 655 t.count = n; 656 respond(x, &t, nil); 657 free(b); 658 return x; 659 } 660 sendp(x->c, xfidread); 661 return nil; 662 } 663 664 static 665 Xfid* 666 fsyswrite(Xfid *x, Fid*) 667 { 668 sendp(x->c, xfidwrite); 669 return nil; 670 } 671 672 static 673 Xfid* 674 fsysclunk(Xfid *x, Fid *f) 675 { 676 fsysdelid(f->mntdir); 677 sendp(x->c, xfidclose); 678 return nil; 679 } 680 681 static 682 Xfid* 683 fsysremove(Xfid *x, Fid*) 684 { 685 Fcall t; 686 687 return respond(x, &t, Eperm); 688 } 689 690 static 691 Xfid* 692 fsysstat(Xfid *x, Fid *f) 693 { 694 Fcall t; 695 696 t.stat = emalloc(messagesize-IOHDRSZ); 697 t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock()); 698 x = respond(x, &t, nil); 699 free(t.stat); 700 return x; 701 } 702 703 static 704 Xfid* 705 fsyswstat(Xfid *x, Fid*) 706 { 707 Fcall t; 708 709 return respond(x, &t, Eperm); 710 } 711 712 Fid* 713 newfid(int fid) 714 { 715 Fid *f, *ff, **fh; 716 717 ff = nil; 718 fh = &fids[fid&(Nhash-1)]; 719 for(f=*fh; f; f=f->next) 720 if(f->fid == fid) 721 return f; 722 else if(ff==nil && f->busy==FALSE) 723 ff = f; 724 if(ff){ 725 ff->fid = fid; 726 return ff; 727 } 728 f = emalloc(sizeof *f); 729 f->fid = fid; 730 f->next = *fh; 731 *fh = f; 732 return f; 733 } 734 735 uint 736 getclock() 737 { 738 char buf[32]; 739 740 buf[0] = '\0'; 741 pread(clockfd, buf, sizeof buf, 0); 742 return atoi(buf); 743 } 744 745 int 746 dostat(int id, Dirtab *dir, uchar *buf, int nbuf, uint clock) 747 { 748 Dir d; 749 750 d.qid.path = QID(id, dir->qid); 751 d.qid.vers = 0; 752 d.qid.type = dir->type; 753 d.mode = dir->perm; 754 d.length = 0; /* would be nice to do better */ 755 d.name = dir->name; 756 d.uid = user; 757 d.gid = user; 758 d.muid = user; 759 d.atime = clock; 760 d.mtime = clock; 761 return convD2M(&d, buf, nbuf); 762 } 763