1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 #include <bio.h> 6 #include <ndb.h> 7 #include <thread.h> 8 9 /* 10 * This fs presents a 1 level file system. It contains 11 * three files per console (xxx and xxxctl and xxxstat) 12 */ 13 14 typedef struct Console Console; 15 typedef struct Fid Fid; 16 typedef struct Request Request; 17 typedef struct Reqlist Reqlist; 18 typedef struct Fs Fs; 19 20 enum 21 { 22 /* last 5 bits of qid.path */ 23 Textern= 0, /* fake parent of top level */ 24 Ttopdir, /* top level directory */ 25 Qctl, 26 Qstat, 27 Qdata, 28 29 Bufsize= 32*1024, /* chars buffered per reader */ 30 Maxcons= 64, /* maximum consoles */ 31 Nhash= 64, /* Fid hash buckets */ 32 }; 33 34 #define TYPE(x) (((ulong)x.path) & 0xf) 35 #define CONS(x) ((((ulong)x.path) >> 4)&0xfff) 36 #define QID(c, x) (((c)<<4) | (x)) 37 38 struct Request 39 { 40 Request *next; 41 Fid *fid; 42 Fs *fs; 43 Fcall f; 44 uchar buf[1]; 45 }; 46 47 struct Reqlist 48 { 49 Lock; 50 Request *first; 51 Request *last; 52 }; 53 54 struct Fid 55 { 56 Lock; 57 Fid *next; /* hash list */ 58 Fid *cnext; /* list of Fid's on a console */ 59 int fid; 60 int ref; 61 62 int attached; 63 int open; 64 char *user; 65 Qid qid; 66 67 Console *c; 68 69 char buf[Bufsize]; 70 char *rp; 71 char *wp; 72 73 Reqlist r; /* active read requests */ 74 }; 75 76 struct Console 77 { 78 Lock; 79 80 char *name; 81 char *dev; 82 int speed; 83 int cronly; 84 int ondemand; /* open only on demand */ 85 86 int pid; /* pid of reader */ 87 88 int fd; 89 int cfd; 90 int sfd; 91 92 Fid *flist; /* open fids to broadcast to */ 93 }; 94 95 struct Fs 96 { 97 Lock; 98 99 int fd; /* to kernel mount point */ 100 int messagesize; 101 Fid *hash[Nhash]; 102 Console *cons[Maxcons]; 103 int ncons; 104 }; 105 106 extern void console(Fs*, char*, char*, int, int, int); 107 extern Fs* fsmount(char*); 108 109 extern void fsreader(void*); 110 extern void fsrun(void*); 111 extern Fid* fsgetfid(Fs*, int); 112 extern void fsputfid(Fs*, Fid*); 113 extern int fsdirgen(Fs*, Qid, int, Dir*, uchar*, int); 114 extern void fsreply(Fs*, Request*, char*); 115 extern void fskick(Fs*, Fid*); 116 extern int fsreopen(Fs*, Console*); 117 118 extern void fsversion(Fs*, Request*, Fid*); 119 extern void fsflush(Fs*, Request*, Fid*); 120 extern void fsauth(Fs*, Request*, Fid*); 121 extern void fsattach(Fs*, Request*, Fid*); 122 extern void fswalk(Fs*, Request*, Fid*); 123 extern void fsclwalk(Fs*, Request*, Fid*); 124 extern void fsopen(Fs*, Request*, Fid*); 125 extern void fscreate(Fs*, Request*, Fid*); 126 extern void fsread(Fs*, Request*, Fid*); 127 extern void fswrite(Fs*, Request*, Fid*); 128 extern void fsclunk(Fs*, Request*, Fid*); 129 extern void fsremove(Fs*, Request*, Fid*); 130 extern void fsstat(Fs*, Request*, Fid*); 131 extern void fswstat(Fs*, Request*, Fid*); 132 133 134 void (*fcall[])(Fs*, Request*, Fid*) = 135 { 136 [Tflush] fsflush, 137 [Tversion] fsversion, 138 [Tauth] fsauth, 139 [Tattach] fsattach, 140 [Twalk] fswalk, 141 [Topen] fsopen, 142 [Tcreate] fscreate, 143 [Tread] fsread, 144 [Twrite] fswrite, 145 [Tclunk] fsclunk, 146 [Tremove] fsremove, 147 [Tstat] fsstat, 148 [Twstat] fswstat 149 }; 150 151 char Eperm[] = "permission denied"; 152 char Eexist[] = "file does not exist"; 153 char Enotdir[] = "not a directory"; 154 char Eisopen[] = "file already open"; 155 char Ebadcount[] = "bad read/write count"; 156 char Enofid[] = "no such fid"; 157 158 char *consoledb = "/lib/ndb/consoledb"; 159 char *mntpt = "/mnt/consoles"; 160 161 int messagesize = 8192+IOHDRSZ; 162 163 void 164 fatal(char *fmt, ...) 165 { 166 va_list arg; 167 char buf[1024]; 168 169 write(2, "consolefs: ", 10); 170 va_start(arg, fmt); 171 vseprint(buf, buf+1024, fmt, arg); 172 va_end(arg); 173 write(2, buf, strlen(buf)); 174 write(2, "\n", 1); 175 threadexitsall(fmt); 176 } 177 178 179 void* 180 emalloc(uint n) 181 { 182 void *p; 183 184 p = malloc(n); 185 if(p == nil) 186 fatal("malloc failed: %r"); 187 memset(p, 0, n); 188 return p; 189 } 190 191 int debug; 192 Ndb *db; 193 194 /* 195 * any request that can get queued for a delayed reply 196 */ 197 Request* 198 allocreq(Fs *fs, int bufsize) 199 { 200 Request *r; 201 202 r = emalloc(sizeof(Request)+bufsize); 203 r->fs = fs; 204 r->next = nil; 205 return r; 206 } 207 208 /* 209 * for maintaining lists of requests 210 */ 211 void 212 addreq(Reqlist *l, Request *r) 213 { 214 lock(l); 215 if(l->first == nil) 216 l->first = r; 217 else 218 l->last->next = r; 219 l->last = r; 220 r->next = nil; 221 unlock(l); 222 } 223 224 /* 225 * remove the first request from a list of requests 226 */ 227 Request* 228 remreq(Reqlist *l) 229 { 230 Request *r; 231 232 lock(l); 233 r = l->first; 234 if(r != nil) 235 l->first = r->next; 236 unlock(l); 237 return r; 238 } 239 240 /* 241 * remove a request with the given tag from a list of requests 242 */ 243 Request* 244 remtag(Reqlist *l, int tag) 245 { 246 Request *or, **ll; 247 248 lock(l); 249 ll = &l->first; 250 for(or = *ll; or; or = or->next){ 251 if(or->f.tag == tag){ 252 *ll = or->next; 253 unlock(l); 254 return or; 255 } 256 ll = &or->next; 257 } 258 unlock(l); 259 return nil; 260 } 261 262 Qid 263 parentqid(Qid q) 264 { 265 if(q.type & QTDIR) 266 return (Qid){QID(0, Textern), 0, QTDIR}; 267 else 268 return (Qid){QID(0, Ttopdir), 0, QTDIR}; 269 } 270 271 int 272 fsdirgen(Fs *fs, Qid parent, int i, Dir *d, uchar *buf, int nbuf) 273 { 274 static char name[64]; 275 char *p; 276 int xcons; 277 278 d->uid = d->gid = d->muid = "network"; 279 d->length = 0; 280 d->atime = time(nil); 281 d->mtime = d->atime; 282 d->type = 'C'; 283 d->dev = '0'; 284 285 switch(TYPE(parent)){ 286 case Textern: 287 if(i != 0) 288 return -1; 289 p = "consoles"; 290 d->mode = DMDIR|0555; 291 d->qid.type = QTDIR; 292 d->qid.path = QID(0, Ttopdir); 293 d->qid.vers = 0; 294 break; 295 case Ttopdir: 296 xcons = i/3; 297 if(xcons >= fs->ncons) 298 return -1; 299 p = fs->cons[xcons]->name; 300 switch(i%3){ 301 case 0: 302 if(fs->cons[xcons]->cfd < 0) 303 return 0; 304 snprint(name, sizeof name, "%sctl", p); 305 p = name; 306 d->qid.type = QTFILE; 307 d->qid.path = QID(xcons, Qctl); 308 d->qid.vers = 0; 309 break; 310 case 1: 311 if(fs->cons[xcons]->sfd < 0) 312 return 0; 313 snprint(name, sizeof name, "%sstat", p); 314 p = name; 315 d->qid.type = QTFILE; 316 d->qid.path = QID(xcons, Qstat); 317 d->qid.vers = 0; 318 break; 319 case 2: 320 d->qid.type = QTFILE; 321 d->qid.path = QID(xcons, Qdata); 322 d->qid.vers = 0; 323 break; 324 } 325 d->mode = 0666; 326 break; 327 default: 328 return -1; 329 } 330 d->name = p; 331 if(buf != nil) 332 return convD2M(d, buf, nbuf); 333 return 1; 334 } 335 336 /* 337 * mount the user interface and start a request processor 338 */ 339 Fs* 340 fsmount(char *mntpt) 341 { 342 Fs *fs; 343 int pfd[2], srv; 344 char buf[32]; 345 int n; 346 static void *v[2]; 347 348 fs = emalloc(sizeof(Fs)); 349 350 if(pipe(pfd) < 0) 351 fatal("opening pipe: %r"); 352 353 /* start up the file system process */ 354 v[0] = fs; 355 v[1] = pfd; 356 proccreate(fsrun, v, 16*1024); 357 358 /* Typically mounted before /srv exists */ 359 if(access("#s/consoles", AEXIST) < 0){ 360 srv = create("#s/consoles", OWRITE, 0666); 361 if(srv < 0) 362 fatal("post: %r"); 363 364 n = sprint(buf, "%d", pfd[1]); 365 if(write(srv, buf, n) < 0) 366 fatal("write srv: %r"); 367 368 close(srv); 369 } 370 371 mount(pfd[1], -1, mntpt, MBEFORE, ""); 372 close(pfd[1]); 373 return fs; 374 } 375 376 /* 377 * reopen a console 378 */ 379 int 380 fsreopen(Fs* fs, Console *c) 381 { 382 char buf[128]; 383 static void *v[2]; 384 385 if(c->pid){ 386 if(postnote(PNPROC, c->pid, "reopen") != 0) 387 fprint(2, "postnote failed: %r\n"); 388 c->pid = 0; 389 } 390 391 if(c->fd >= 0){ 392 close(c->fd); 393 close(c->cfd); 394 close(c->sfd); 395 c->cfd = -1; 396 c->fd = -1; 397 c->sfd = -1; 398 } 399 400 if(c->flist == nil && c->ondemand) 401 return 0; 402 403 c->fd = open(c->dev, ORDWR); 404 if(c->fd < 0) 405 return -1; 406 407 snprint(buf, sizeof(buf), "%sctl", c->dev); 408 c->cfd = open(buf, ORDWR); 409 fprint(c->cfd, "b%d", c->speed); 410 411 snprint(buf, sizeof(buf), "%sstat", c->dev); 412 c->sfd = open(buf, OREAD); 413 414 v[0] = fs; 415 v[1] = c; 416 proccreate(fsreader, v, 16*1024); 417 418 return 0; 419 } 420 421 void 422 change(Fs *fs, Console *c, int doreopen, int speed, int cronly, int ondemand) 423 { 424 lock(c); 425 426 if(speed != c->speed){ 427 c->speed = speed; 428 doreopen = 1; 429 } 430 if(ondemand != c->ondemand){ 431 c->ondemand = ondemand; 432 doreopen = 1; 433 } 434 c->cronly = cronly; 435 if(doreopen) 436 fsreopen(fs, c); 437 438 unlock(c); 439 } 440 441 /* 442 * create a console interface 443 */ 444 void 445 console(Fs* fs, char *name, char *dev, int speed, int cronly, int ondemand) 446 { 447 Console *c; 448 char *x; 449 int i, doreopen; 450 451 if(fs->ncons >= Maxcons) 452 fatal("too many consoles, too little time"); 453 454 doreopen = 0; 455 for(i = 0; i < fs->ncons; i++){ 456 c = fs->cons[i]; 457 if(strcmp(name, c->name) == 0){ 458 if(strcmp(dev, c->dev) != 0){ 459 /* new device */ 460 x = c->dev; 461 c->dev = strdup(dev); 462 free(x); 463 doreopen = 1; 464 } 465 change(fs, c, doreopen, speed, cronly, ondemand); 466 return; 467 } 468 } 469 for(i = 0; i < fs->ncons; i++){ 470 c = fs->cons[i]; 471 if(strcmp(dev, c->dev) == 0){ 472 /* at least a rename */ 473 x = c->name; 474 c->name = strdup(name); 475 free(x); 476 change(fs, c, doreopen, speed, cronly, ondemand); 477 return; 478 } 479 } 480 481 c = emalloc(sizeof(Console)); 482 fs->cons[fs->ncons] = c; 483 fs->ncons++; 484 c->name = strdup(name); 485 c->dev = strdup(dev); 486 c->fd = -1; 487 c->cfd = -1; 488 c->sfd = -1; 489 change(fs, c, 1, speed, cronly, ondemand); 490 } 491 492 /* 493 * buffer data from console to a client. 494 * circular q with writer able to catch up to reader. 495 * the reader may miss data but always sees an in order sequence. 496 */ 497 void 498 fromconsole(Fid *f, char *p, int n) 499 { 500 char *rp, *wp, *ep; 501 int pass; 502 503 lock(f); 504 rp = f->rp; 505 wp = f->wp; 506 ep = f->buf + sizeof(f->buf); 507 pass = 0; 508 while(n--){ 509 *wp++ = *p++; 510 if(wp >= ep) 511 wp = f->buf; 512 if(rp == wp) 513 pass = 1; 514 } 515 f->wp = wp; 516 517 /* we overtook the read pointer, push it up so readers always 518 * see the tail of what was written 519 */ 520 if(pass){ 521 wp++; 522 if(wp >= ep) 523 f->rp = f->buf; 524 else 525 f->rp = wp; 526 } 527 unlock(f); 528 } 529 530 /* 531 * broadcast a list of members to all listeners 532 */ 533 void 534 bcastmembers(Fs *fs, Console *c, char *msg, Fid *f) 535 { 536 int n; 537 Fid *fl; 538 char buf[512]; 539 540 sprint(buf, "[%s%s", msg, f->user); 541 for(fl = c->flist; fl != nil && strlen(buf) + 64 < sizeof(buf); fl = fl->cnext){ 542 if(f == fl) 543 continue; 544 strcat(buf, ", "); 545 strcat(buf, fl->user); 546 } 547 strcat(buf, "]\n"); 548 549 n = strlen(buf); 550 for(fl = c->flist; fl; fl = fl->cnext){ 551 fromconsole(fl, buf, n); 552 fskick(fs, fl); 553 } 554 } 555 556 void 557 handler(void*, char *msg) 558 { 559 if(strstr(msg, "reopen")) 560 noted(NCONT); 561 noted(NDFLT); 562 } 563 564 565 /* 566 * a process to read console output and broadcast it (one per console) 567 */ 568 void 569 fsreader(void *v) 570 { 571 int n; 572 Fid *fl; 573 char buf[1024]; 574 Fs *fs; 575 Console *c; 576 void **a; 577 578 a = v; 579 fs = a[0]; 580 c = a[1]; 581 c->pid = getpid(); 582 notify(handler); 583 for(;;){ 584 n = read(c->fd, buf, sizeof(buf)); 585 if(n < 0) 586 break; 587 lock(c); 588 for(fl = c->flist; fl; fl = fl->cnext){ 589 fromconsole(fl, buf, n); 590 fskick(fs, fl); 591 } 592 unlock(c); 593 } 594 } 595 596 void 597 readdb(Fs *fs) 598 { 599 Ndbtuple *t, *nt; 600 char *dev, *cons; 601 int cronly, speed, ondemand; 602 603 ndbreopen(db); 604 605 /* start a listener for each console */ 606 for(;;){ 607 t = ndbparse(db); 608 if(t == nil) 609 break; 610 dev = nil; 611 cons = nil; 612 speed = 9600; 613 cronly = 0; 614 ondemand = 0; 615 for(nt = t; nt; nt = nt->entry){ 616 if(strcmp(nt->attr, "console") == 0) 617 cons = nt->val; 618 else if(strcmp(nt->attr, "dev") == 0) 619 dev = nt->val; 620 else if(strcmp(nt->attr, "speed") == 0) 621 speed = atoi(nt->val); 622 else if(strcmp(nt->attr, "cronly") == 0) 623 cronly = 1; 624 else if(strcmp(nt->attr, "openondemand") == 0) 625 ondemand = 1; 626 } 627 if(dev != nil && cons != nil) 628 console(fs, cons, dev, speed, cronly, ondemand); 629 ndbfree(t); 630 } 631 } 632 633 int dbmtime; 634 635 /* 636 * a request processor (one per Fs) 637 */ 638 void 639 fsrun(void *v) 640 { 641 int n, t; 642 Request *r; 643 Fid *f; 644 Dir *d; 645 void **a = v; 646 Fs* fs; 647 int *pfd; 648 649 fs = a[0]; 650 pfd = a[1]; 651 fs->fd = pfd[0]; 652 for(;;){ 653 d = dirstat(consoledb); 654 if(d != nil && d->mtime != dbmtime){ 655 dbmtime = d->mtime; 656 readdb(fs); 657 } 658 free(d); 659 r = allocreq(fs, messagesize); 660 n = read9pmsg(fs->fd, r->buf, messagesize); 661 if(n <= 0) 662 fatal("unmounted"); 663 664 if(convM2S(r->buf, n, &r->f) == 0){ 665 fprint(2, "can't convert %ux %ux %ux\n", r->buf[0], 666 r->buf[1], r->buf[2]); 667 free(r); 668 continue; 669 } 670 671 672 f = fsgetfid(fs, r->f.fid); 673 r->fid = f; 674 if(debug) 675 fprint(2, "%F path %llux\n", &r->f, f->qid.path); 676 677 t = r->f.type; 678 r->f.type++; 679 (*fcall[t])(fs, r, f); 680 } 681 } 682 683 Fid* 684 fsgetfid(Fs *fs, int fid) 685 { 686 Fid *f, *nf; 687 688 lock(fs); 689 for(f = fs->hash[fid%Nhash]; f; f = f->next){ 690 if(f->fid == fid){ 691 f->ref++; 692 unlock(fs); 693 return f; 694 } 695 } 696 697 nf = emalloc(sizeof(Fid)); 698 nf->next = fs->hash[fid%Nhash]; 699 fs->hash[fid%Nhash] = nf; 700 nf->fid = fid; 701 nf->ref = 1; 702 nf->wp = nf->buf; 703 nf->rp = nf->wp; 704 unlock(fs); 705 return nf; 706 } 707 708 void 709 fsputfid(Fs *fs, Fid *f) 710 { 711 Fid **l, *nf; 712 713 lock(fs); 714 if(--f->ref > 0){ 715 unlock(fs); 716 return; 717 } 718 for(l = &fs->hash[f->fid%Nhash]; nf = *l; l = &nf->next) 719 if(nf == f){ 720 *l = f->next; 721 break; 722 } 723 unlock(fs); 724 free(f->user); 725 free(f); 726 } 727 728 729 void 730 fsauth(Fs *fs, Request *r, Fid*) 731 { 732 fsreply(fs, r, "consolefs: authentication not required"); 733 } 734 735 void 736 fsversion(Fs *fs, Request *r, Fid*) 737 { 738 739 if(r->f.msize < 256){ 740 fsreply(fs, r, "message size too small"); 741 return; 742 } 743 messagesize = r->f.msize; 744 if(messagesize > 8192+IOHDRSZ) 745 messagesize = 8192+IOHDRSZ; 746 r->f.msize = messagesize; 747 if(strncmp(r->f.version, "9P2000", 6) != 0){ 748 fsreply(fs, r, "unrecognized 9P version"); 749 return; 750 } 751 r->f.version = "9P2000"; 752 753 fsreply(fs, r, nil); 754 } 755 756 void 757 fsflush(Fs *fs, Request *r, Fid *f) 758 { 759 Request *or; 760 761 or = remtag(&f->r, r->f.oldtag); 762 if(or != nil){ 763 fsputfid(fs, or->fid); 764 free(or); 765 } 766 fsreply(fs, r, nil); 767 } 768 769 void 770 fsattach(Fs *fs, Request *r, Fid *f) 771 { 772 f->qid.type = QTDIR; 773 f->qid.path = QID(0, Ttopdir); 774 f->qid.vers = 0; 775 776 if(r->f.uname[0]) 777 f->user = strdup(r->f.uname); 778 else 779 f->user = strdup("none"); 780 781 /* hold down the fid till the clunk */ 782 f->attached = 1; 783 lock(fs); 784 f->ref++; 785 unlock(fs); 786 787 r->f.qid = f->qid; 788 fsreply(fs, r, nil); 789 } 790 791 void 792 fswalk(Fs *fs, Request *r, Fid *f) 793 { 794 char *name; 795 Dir d; 796 int i, n, nqid, nwname; 797 Qid qid, wqid[MAXWELEM]; 798 Fid *nf; 799 char *err; 800 801 if(f->attached == 0){ 802 fsreply(fs, r, Enofid); 803 return; 804 } 805 806 nf = nil; 807 if(r->f.fid != r->f.newfid){ 808 nf = fsgetfid(fs, r->f.newfid); 809 nf->attached = f->attached; 810 nf->open = f->open; 811 nf->qid = f->qid; 812 nf->user = strdup(f->user); 813 nf->c = f->c; 814 nf->wp = nf->buf; 815 nf->rp = nf->wp; 816 f = nf; 817 } 818 819 qid = f->qid; 820 err = nil; 821 nwname = r->f.nwname; 822 nqid = 0; 823 if(nwname > 0){ 824 for(; err == nil && nqid < nwname; nqid++){ 825 if(nqid >= MAXWELEM){ 826 err = "too many name elements"; 827 break; 828 } 829 name = r->f.wname[nqid]; 830 if(strcmp(name, "..") == 0) 831 qid = parentqid(qid); 832 else if(strcmp(name, ".") != 0){ 833 for(i = 0; ; i++){ 834 n = fsdirgen(fs, qid, i, &d, nil, 0); 835 if(n < 0){ 836 err = Eexist; 837 break; 838 } 839 if(n > 0 && strcmp(name, d.name) == 0){ 840 qid = d.qid; 841 break; 842 } 843 } 844 } 845 wqid[nqid] = qid; 846 } 847 if(nf != nil && nqid < nwname) 848 fsputfid(fs, nf); 849 if(nqid == nwname) 850 f->qid = qid; 851 } 852 853 memmove(r->f.wqid, wqid, nqid*sizeof(Qid)); 854 r->f.nwqid = nqid; 855 fsreply(fs, r, err); 856 } 857 858 int 859 ingroup(char *user, char *group) 860 { 861 Ndbtuple *t, *nt; 862 Ndbs s; 863 864 t = ndbsearch(db, &s, "group", group); 865 if(t == nil) 866 return 0; 867 for(nt = t; nt; nt = nt->entry){ 868 if(strcmp(nt->attr, "uid") == 0) 869 if(strcmp(nt->val, user) == 0) 870 break; 871 } 872 ndbfree(t); 873 return nt != nil; 874 } 875 876 int 877 userok(char *u, char *cname) 878 { 879 Ndbtuple *t, *nt; 880 Ndbs s; 881 882 t = ndbsearch(db, &s, "console", cname); 883 if(t == nil) 884 return 0; 885 886 for(nt = t; nt; nt = nt->entry){ 887 if(strcmp(nt->attr, "uid") == 0) 888 if(strcmp(nt->val, u) == 0) 889 break; 890 if(strcmp(nt->attr, "gid") == 0) 891 if(ingroup(u, nt->val)) 892 break; 893 } 894 ndbfree(t); 895 return nt != nil; 896 } 897 898 int m2p[] ={ 899 [OREAD] 4, 900 [OWRITE] 2, 901 [ORDWR] 6 902 }; 903 904 void 905 fsopen(Fs *fs, Request *r, Fid *f) 906 { 907 int mode; 908 Console *c; 909 910 if(f->attached == 0){ 911 fsreply(fs, r, Enofid); 912 return; 913 } 914 915 if(f->open){ 916 fsreply(fs, r, Eisopen); 917 return; 918 } 919 920 mode = r->f.mode & 3; 921 922 if((QTDIR & f->qid.type) && mode != OREAD){ 923 fsreply(fs, r, Eperm); 924 return; 925 } 926 927 switch(TYPE(f->qid)){ 928 case Qdata: 929 c = fs->cons[CONS(f->qid)]; 930 if(!userok(f->user, c->name)){ 931 fsreply(fs, r, Eperm); 932 return; 933 } 934 f->rp = f->buf; 935 f->wp = f->buf; 936 f->c = c; 937 lock(c); 938 f->cnext = c->flist; 939 c->flist = f; 940 bcastmembers(fs, c, "+", f); 941 if(c->pid == 0) 942 fsreopen(fs, c); 943 unlock(c); 944 break; 945 case Qctl: 946 c = fs->cons[CONS(f->qid)]; 947 if(!userok(f->user, c->name)){ 948 fsreply(fs, r, Eperm); 949 return; 950 } 951 f->c = c; 952 break; 953 case Qstat: 954 c = fs->cons[CONS(f->qid)]; 955 if(!userok(f->user, c->name)){ 956 fsreply(fs, r, Eperm); 957 return; 958 } 959 f->c = c; 960 break; 961 } 962 963 f->open = 1; 964 r->f.iounit = messagesize-IOHDRSZ; 965 r->f.qid = f->qid; 966 fsreply(fs, r, nil); 967 } 968 969 void 970 fscreate(Fs *fs, Request *r, Fid*) 971 { 972 fsreply(fs, r, Eperm); 973 } 974 975 void 976 fsread(Fs *fs, Request *r, Fid *f) 977 { 978 uchar *p, *e; 979 int i, m, off; 980 vlong offset; 981 Dir d; 982 char sbuf[ERRMAX]; 983 984 if(f->attached == 0){ 985 fsreply(fs, r, Enofid); 986 return; 987 } 988 989 if((int)r->f.count < 0){ 990 fsreply(fs, r, Ebadcount); 991 return; 992 } 993 994 if(QTDIR & f->qid.type){ 995 p = r->buf + IOHDRSZ; 996 e = p + r->f.count; 997 offset = r->f.offset; 998 off = 0; 999 for(i=0; p<e; i++, off+=m){ 1000 m = fsdirgen(fs, f->qid, i, &d, p, e-p); 1001 if(m < 0) 1002 break; 1003 if(m > BIT16SZ && off >= offset) 1004 p += m; 1005 } 1006 r->f.data = (char*)r->buf + IOHDRSZ; 1007 r->f.count = (char*)p - r->f.data; 1008 } else { 1009 switch(TYPE(f->qid)){ 1010 case Qdata: 1011 addreq(&f->r, r); 1012 fskick(fs, f); 1013 return; 1014 case Qctl: 1015 r->f.data = (char*)r->buf+IOHDRSZ; 1016 r->f.count = 0; 1017 break; 1018 case Qstat: 1019 if(r->f.count > sizeof(sbuf)) 1020 r->f.count = sizeof(sbuf); 1021 i = pread(f->c->sfd, sbuf, r->f.count, r->f.offset); 1022 if(i < 0){ 1023 errstr(sbuf, sizeof sbuf); 1024 fsreply(fs, r, sbuf); 1025 return; 1026 } 1027 r->f.data = sbuf; 1028 r->f.count = i; 1029 break; 1030 default: 1031 fsreply(fs, r, Eexist); 1032 return; 1033 } 1034 } 1035 fsreply(fs, r, nil); 1036 } 1037 1038 void 1039 fswrite(Fs *fs, Request *r, Fid *f) 1040 { 1041 int i; 1042 1043 if(f->attached == 0){ 1044 fsreply(fs, r, Enofid); 1045 return; 1046 } 1047 1048 if((int)r->f.count < 0){ 1049 fsreply(fs, r, Ebadcount); 1050 return; 1051 } 1052 1053 if(QTDIR & f->qid.type){ 1054 fsreply(fs, r, Eperm); 1055 return; 1056 } 1057 1058 switch(TYPE(f->qid)){ 1059 default: 1060 fsreply(fs, r, Eperm); 1061 return; 1062 case Qctl: 1063 write(f->c->cfd, r->f.data, r->f.count); 1064 break; 1065 case Qdata: 1066 if(f->c->cronly) 1067 for(i = 0; i < r->f.count; i++) 1068 if(r->f.data[i] == '\n') 1069 r->f.data[i] = '\r'; 1070 write(f->c->fd, r->f.data, r->f.count); 1071 break; 1072 } 1073 fsreply(fs, r, nil); 1074 } 1075 1076 void 1077 fsclunk(Fs *fs, Request *r, Fid *f) 1078 { 1079 Fid **l, *fl; 1080 Request *nr; 1081 1082 if(f->open && TYPE(f->qid) == Qdata){ 1083 while((nr = remreq(&f->r)) != nil){ 1084 fsputfid(fs, f); 1085 free(nr); 1086 } 1087 1088 lock(f->c); 1089 for(l = &f->c->flist; *l; l = &fl->cnext){ 1090 fl = *l; 1091 if(fl == f){ 1092 *l = fl->cnext; 1093 break; 1094 } 1095 } 1096 bcastmembers(fs, f->c, "-", f); 1097 if(f->c->ondemand && f->c->flist == nil) 1098 fsreopen(fs, f->c); 1099 unlock(f->c); 1100 } 1101 fsreply(fs, r, nil); 1102 fsputfid(fs, f); 1103 } 1104 1105 void 1106 fsremove(Fs *fs, Request *r, Fid*) 1107 { 1108 fsreply(fs, r, Eperm); 1109 } 1110 1111 1112 void 1113 fsstat(Fs *fs, Request *r, Fid *f) 1114 { 1115 int i, n; 1116 Qid q; 1117 Dir d; 1118 1119 q = parentqid(f->qid); 1120 for(i = 0; ; i++){ 1121 r->f.stat = r->buf+IOHDRSZ; 1122 n = fsdirgen(fs, q, i, &d, r->f.stat, messagesize-IOHDRSZ); 1123 if(n < 0){ 1124 fsreply(fs, r, Eexist); 1125 return; 1126 } 1127 r->f.nstat = n; 1128 if(r->f.nstat > BIT16SZ && d.qid.path == f->qid.path) 1129 break; 1130 } 1131 fsreply(fs, r, nil); 1132 } 1133 1134 void 1135 fswstat(Fs *fs, Request *r, Fid*) 1136 { 1137 fsreply(fs, r, Eperm); 1138 } 1139 1140 void 1141 fsreply(Fs *fs, Request *r, char *err) 1142 { 1143 int n; 1144 uchar buf[8192+IOHDRSZ]; 1145 1146 if(err){ 1147 r->f.type = Rerror; 1148 r->f.ename = err; 1149 } 1150 n = convS2M(&r->f, buf, messagesize); 1151 if(debug) 1152 fprint(2, "%F path %llux n=%d\n", &r->f, r->fid->qid.path, n); 1153 fsputfid(fs, r->fid); 1154 if(write(fs->fd, buf, n) != n) 1155 fatal("unmounted"); 1156 free(r); 1157 } 1158 1159 /* 1160 * called whenever input or a read request has been received 1161 */ 1162 void 1163 fskick(Fs *fs, Fid *f) 1164 { 1165 Request *r; 1166 char *p, *rp, *wp, *ep; 1167 int i; 1168 1169 lock(f); 1170 while(f->rp != f->wp){ 1171 r = remreq(&f->r); 1172 if(r == nil) 1173 break; 1174 p = (char*)r->buf; 1175 rp = f->rp; 1176 wp = f->wp; 1177 ep = &f->buf[Bufsize]; 1178 for(i = 0; i < r->f.count && rp != wp; i++){ 1179 *p++ = *rp++; 1180 if(rp >= ep) 1181 rp = f->buf; 1182 } 1183 f->rp = rp; 1184 r->f.data = (char*)r->buf; 1185 r->f.count = p - (char*)r->buf; 1186 fsreply(fs, r, nil); 1187 } 1188 unlock(f); 1189 } 1190 1191 void 1192 usage(void) 1193 { 1194 fprint(2, "usage: consolefs [-d] [-m mount-point] [-c console-db]\n"); 1195 threadexitsall("usage"); 1196 } 1197 1198 void 1199 threadmain(int argc, char **argv) 1200 { 1201 fmtinstall('F', fcallfmt); 1202 1203 ARGBEGIN{ 1204 case 'd': 1205 debug++; 1206 break; 1207 case 'c': 1208 consoledb = ARGF(); 1209 if(consoledb == nil) 1210 usage(); 1211 break; 1212 case 'm': 1213 mntpt = ARGF(); 1214 if(mntpt == nil) 1215 usage(); 1216 break; 1217 }ARGEND; 1218 1219 db = ndbopen(consoledb); 1220 if(db == nil) 1221 fatal("can't open %s: %r", consoledb); 1222 1223 fsmount(mntpt); 1224 } 1225 1226