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