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