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