1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 #include <thread.h> 6 #include <9p.h> 7 8 static char Ebadattach[] = "unknown specifier in attach"; 9 static char Ebadoffset[] = "bad offset"; 10 static char Ebadcount[] = "bad count"; 11 static char Ebotch[] = "9P protocol botch"; 12 static char Ecreatenondir[] = "create in non-directory"; 13 static char Edupfid[] = "duplicate fid"; 14 static char Eduptag[] = "duplicate tag"; 15 static char Eisdir[] = "is a directory"; 16 static char Enocreate[] = "create prohibited"; 17 static char Enomem[] = "out of memory"; 18 static char Enoremove[] = "remove prohibited"; 19 static char Enostat[] = "stat prohibited"; 20 static char Enotfound[] = "file not found"; 21 static char Enowrite[] = "write prohibited"; 22 static char Enowstat[] = "wstat prohibited"; 23 static char Eperm[] = "permission denied"; 24 static char Eunknownfid[] = "unknown fid"; 25 static char Ebaddir[] = "bad directory in wstat"; 26 static char Ewalknodir[] = "walk in non-directory"; 27 28 static void 29 setfcallerror(Fcall *f, char *err) 30 { 31 f->ename = err; 32 f->type = Rerror; 33 } 34 35 static void 36 changemsize(Srv *srv, int msize) 37 { 38 if(srv->rbuf && srv->wbuf && srv->msize == msize) 39 return; 40 qlock(&srv->rlock); 41 qlock(&srv->wlock); 42 srv->msize = msize; 43 free(srv->rbuf); 44 free(srv->wbuf); 45 srv->rbuf = emalloc9p(msize); 46 srv->wbuf = emalloc9p(msize); 47 if(srv->rbuf==nil || srv->wbuf==nil) 48 sysfatal("out of memory"); /* BUG */ 49 qunlock(&srv->rlock); 50 qunlock(&srv->wlock); 51 } 52 53 static Req* 54 getreq(Srv *s) 55 { 56 long n; 57 uchar *buf; 58 Fcall f; 59 Req *r; 60 61 qlock(&s->rlock); 62 if((n = read9pmsg(s->infd, s->rbuf, s->msize)) <= 0){ 63 qunlock(&s->rlock); 64 return nil; 65 } 66 67 buf = emalloc9p(n); 68 memmove(buf, s->rbuf, n); 69 qunlock(&s->rlock); 70 71 if(convM2S(buf, n, &f) != n){ 72 free(buf); 73 return nil; 74 } 75 76 if((r=allocreq(s->rpool, f.tag)) == nil){ /* duplicate tag: cons up a fake Req */ 77 r = emalloc9p(sizeof *r); 78 incref(&r->ref); 79 r->tag = f.tag; 80 r->ifcall = f; 81 r->error = Eduptag; 82 r->buf = buf; 83 r->responded = 0; 84 r->type = 0; 85 r->srv = s; 86 r->pool = nil; 87 if(chatty9p) 88 fprint(2, "<-%d- %F: dup tag\n", s->infd, &f); 89 return r; 90 } 91 92 r->srv = s; 93 r->responded = 0; 94 r->buf = buf; 95 r->ifcall = f; 96 memset(&r->ofcall, 0, sizeof r->ofcall); 97 r->type = r->ifcall.type; 98 99 if(chatty9p) 100 if(r->error) 101 fprint(2, "<-%d- %F: %s\n", s->infd, &r->ifcall, r->error); 102 else 103 fprint(2, "<-%d- %F\n", s->infd, &r->ifcall); 104 105 return r; 106 } 107 108 static void 109 filewalk(Req *r) 110 { 111 int i; 112 File *f; 113 114 f = r->fid->file; 115 assert(f != nil); 116 117 incref(f); 118 for(i=0; i<r->ifcall.nwname; i++) 119 if(f = walkfile(f, r->ifcall.wname[i])) 120 r->ofcall.wqid[i] = f->qid; 121 else 122 break; 123 124 r->ofcall.nwqid = i; 125 if(f){ 126 r->newfid->file = f; 127 r->newfid->qid = r->newfid->file->qid; 128 } 129 respond(r, nil); 130 } 131 132 static void 133 conswalk(Req *r, char *(*clone)(Fid*, Fid*), char *(*walk1)(Fid*, char*, Qid*)) 134 { 135 int i; 136 char *e; 137 138 if(r->fid == r->newfid && r->ifcall.nwname > 1){ 139 respond(r, "lib9p: unused documented feature not implemented"); 140 return; 141 } 142 143 if(r->fid != r->newfid){ 144 r->newfid->qid = r->fid->qid; 145 if(clone && (e = clone(r->fid, r->newfid))){ 146 respond(r, e); 147 return; 148 } 149 } 150 151 e = nil; 152 for(i=0; i<r->ifcall.nwname; i++){ 153 if(e = walk1(r->newfid, r->ifcall.wname[i], &r->ofcall.wqid[i])) 154 break; 155 r->newfid->qid = r->ofcall.wqid[i]; 156 } 157 158 r->ofcall.nwqid = i; 159 if(e && i==0) 160 respond(r, e); 161 else 162 respond(r, nil); 163 } 164 165 void 166 srv(Srv *srv) 167 { 168 int o, p; 169 Req *r; 170 char e[ERRMAX]; 171 172 fmtinstall('D', dirfmt); 173 fmtinstall('F', fcallfmt); 174 175 if(srv->fpool == nil) 176 srv->fpool = allocfidpool(srv->destroyfid); 177 if(srv->rpool == nil) 178 srv->rpool = allocreqpool(srv->destroyreq); 179 if(srv->msize == 0) 180 srv->msize = 8192+IOHDRSZ; 181 182 changemsize(srv, srv->msize); 183 184 srv->fpool->srv = srv; 185 srv->rpool->srv = srv; 186 187 while(r = getreq(srv)){ 188 if(r->error){ 189 respond(r, r->error); 190 continue; 191 } 192 193 switch(r->type){ 194 default: 195 respond(r, "unknown message"); 196 break; 197 198 case Tversion: 199 if(strncmp(r->ifcall.version, "9P", 2) != 0){ 200 r->ofcall.version = "unknown"; 201 respond(r, nil); 202 break; 203 } 204 205 r->ofcall.version = "9P2000"; 206 r->ofcall.msize = r->ifcall.msize; 207 respond(r, nil); 208 break; 209 210 case Tauth: 211 if((r->afid = allocfid(srv->fpool, r->ifcall.afid)) == nil){ 212 respond(r, Edupfid); 213 break; 214 } 215 if(srv->auth) 216 srv->auth(r); 217 else{ 218 snprint(e, sizeof e, "%s: authentication not required", argv0); 219 respond(r, e); 220 } 221 break; 222 223 case Tattach: 224 if((r->fid = allocfid(srv->fpool, r->ifcall.fid)) == nil){ 225 respond(r, Edupfid); 226 break; 227 } 228 r->afid = nil; 229 if(r->ifcall.afid != NOFID && (r->afid = lookupfid(srv->fpool, r->ifcall.afid)) == nil){ 230 respond(r, Eunknownfid); 231 break; 232 } 233 r->fid->uid = estrdup9p(r->ifcall.uname); 234 if(srv->tree){ 235 r->fid->file = srv->tree->root; 236 /* BUG? incref(r->fid->file) ??? */ 237 r->ofcall.qid = r->fid->file->qid; 238 r->fid->qid = r->ofcall.qid; 239 } 240 if(srv->attach) 241 srv->attach(r); 242 else 243 respond(r, nil); 244 break; 245 246 case Tflush: 247 r->oldreq = lookupreq(srv->rpool, r->ifcall.oldtag); 248 if(r->oldreq == nil || r->oldreq == r) 249 respond(r, nil); 250 else if(srv->flush) 251 srv->flush(r); 252 else 253 sysfatal("outstanding message but flush not provided"); 254 break; 255 256 case Twalk: 257 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ 258 respond(r, Eunknownfid); 259 break; 260 } 261 if(r->fid->omode != -1){ 262 respond(r, "cannot clone open fid"); 263 break; 264 } 265 if(r->ifcall.nwname && !(r->fid->qid.type&QTDIR)){ 266 respond(r, Ewalknodir); 267 break; 268 } 269 if(r->ifcall.fid != r->ifcall.newfid){ 270 if((r->newfid = allocfid(srv->fpool, r->ifcall.newfid)) == nil){ 271 respond(r, Edupfid); 272 break; 273 } 274 r->newfid->uid = estrdup9p(r->fid->uid); 275 }else{ 276 incref(&r->fid->ref); 277 r->newfid = r->fid; 278 } 279 280 if(r->fid->file){ 281 filewalk(r); 282 }else if(srv->walk1) 283 conswalk(r, srv->clone, srv->walk1); 284 else if(srv->walk) 285 srv->walk(r); 286 else 287 sysfatal("no walk function, no file trees"); 288 break; 289 290 case Topen: 291 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ 292 respond(r, Eunknownfid); 293 break; 294 } 295 if(r->fid->omode != -1){ 296 respond(r, Ebotch); 297 break; 298 } 299 if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){ 300 respond(r, Eisdir); 301 break; 302 } 303 r->ofcall.qid = r->fid->qid; 304 switch(r->ifcall.mode&3){ 305 default: 306 assert(0); 307 case OREAD: 308 p = AREAD; 309 break; 310 case OWRITE: 311 p = AWRITE; 312 break; 313 case ORDWR: 314 p = AREAD|AWRITE; 315 break; 316 case OEXEC: 317 p = AEXEC; 318 break; 319 } 320 if(r->ifcall.mode&OTRUNC) 321 p |= AWRITE; 322 if((r->fid->qid.type&QTDIR) && p!=AREAD){ 323 respond(r, Eperm); 324 break; 325 } 326 if(r->fid->file){ 327 if(!hasperm(r->fid->file, r->fid->uid, p)){ 328 respond(r, Eperm); 329 break; 330 } 331 /* BUG RACE */ 332 if((r->ifcall.mode&ORCLOSE) 333 && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){ 334 respond(r, Eperm); 335 break; 336 } 337 r->ofcall.qid = r->fid->file->qid; 338 if((r->ofcall.qid.type&QTDIR) 339 && (r->fid->rdir = opendirfile(r->fid->file)) == nil){ 340 respond(r, "opendirfile failed"); 341 break; 342 } 343 } 344 if(srv->open) 345 srv->open(r); 346 else 347 respond(r, nil); 348 break; 349 350 case Tcreate: 351 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil) 352 respond(r, Eunknownfid); 353 else if(r->fid->omode != -1) 354 respond(r, Ebotch); 355 else if(!(r->fid->qid.type&QTDIR)) 356 respond(r, Ecreatenondir); 357 else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE)) 358 respond(r, Eperm); 359 else if(srv->create) 360 srv->create(r); 361 else 362 respond(r, Enocreate); 363 break; 364 365 case Tread: 366 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ 367 respond(r, Eunknownfid); 368 break; 369 } 370 if(r->ifcall.count < 0){ 371 respond(r, Ebotch); 372 break; 373 } 374 if(r->ifcall.offset < 0 375 || ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){ 376 respond(r, Ebadoffset); 377 break; 378 } 379 380 if(r->ifcall.count > srv->msize - IOHDRSZ) 381 r->ifcall.count = srv->msize - IOHDRSZ; 382 if((r->rbuf = emalloc9p(r->ifcall.count)) == nil){ 383 respond(r, Enomem); 384 break; 385 } 386 r->ofcall.data = r->rbuf; 387 o = r->fid->omode & 3; 388 if(o != OREAD && o != ORDWR && o != OEXEC){ 389 respond(r, Ebotch); 390 break; 391 } 392 if((r->fid->qid.type&QTDIR) && r->fid->file){ 393 r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count); 394 respond(r, nil); 395 break; 396 } 397 if(srv->read) 398 srv->read(r); 399 else 400 respond(r, "no srv->read"); 401 break; 402 403 case Twrite: 404 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ 405 respond(r, Eunknownfid); 406 break; 407 } 408 if(r->ifcall.count < 0){ 409 respond(r, Ebotch); 410 break; 411 } 412 if(r->ifcall.offset < 0){ 413 respond(r, Ebotch); 414 break; 415 } 416 if(r->ifcall.count > srv->msize - IOHDRSZ) 417 r->ifcall.count = srv->msize - IOHDRSZ; 418 o = r->fid->omode & 3; 419 if(o != OWRITE && o != ORDWR){ 420 snprint(e, sizeof e, "write on fid with open mode 0x%ux", r->fid->omode); 421 respond(r, e); 422 break; 423 } 424 if(srv->write) 425 srv->write(r); 426 else 427 respond(r, "no srv->write"); 428 break; 429 430 case Tclunk: 431 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil) 432 respond(r, Eunknownfid); 433 else 434 respond(r, nil); 435 break; 436 437 case Tremove: 438 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){ 439 respond(r, Eunknownfid); 440 break; 441 } 442 /* BUG RACE */ 443 if(r->fid->file && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){ 444 respond(r, Eperm); 445 break; 446 } 447 if(srv->remove) 448 srv->remove(r); 449 else 450 respond(r, r->fid->file ? nil : Enoremove); 451 break; 452 453 case Tstat: 454 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ 455 respond(r, Eunknownfid); 456 break; 457 } 458 if(r->fid->file){ 459 r->d = r->fid->file->Dir; 460 if(r->d.name) 461 r->d.name = estrdup9p(r->d.name); 462 if(r->d.uid) 463 r->d.uid = estrdup9p(r->d.uid); 464 if(r->d.gid) 465 r->d.gid = estrdup9p(r->d.gid); 466 if(r->d.muid) 467 r->d.muid = estrdup9p(r->d.muid); 468 } 469 if(srv->stat) 470 srv->stat(r); 471 else if(r->fid->file) 472 respond(r, nil); 473 else 474 respond(r, Enostat); 475 break; 476 477 case Twstat: 478 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil) 479 respond(r, Eunknownfid); 480 else if(srv->wstat){ 481 if(convM2D(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat) != r->ifcall.nstat) 482 respond(r, Ebaddir); 483 else 484 srv->wstat(r); 485 }else 486 respond(r, Enowstat); 487 break; 488 } 489 } 490 491 if(srv->end) 492 srv->end(srv); 493 } 494 495 void 496 respond(Req *r, char *error) 497 { 498 int m, n; 499 Req *or; 500 uchar *statbuf, tmp[BIT16SZ]; 501 char errbuf[ERRMAX]; 502 Srv *srv; 503 504 srv = r->srv; 505 assert(srv != nil); 506 507 assert(r->responded == 0); 508 r->responded = 1; 509 510 switch(r->ifcall.type){ 511 default: 512 assert(0); 513 514 case Tversion: 515 assert(error == nil); 516 changemsize(srv, r->ofcall.msize); 517 break; 518 519 case Tauth: 520 if(error){ 521 if(r->afid) 522 closefid(removefid(srv->fpool, r->afid->fid)); 523 break; 524 } 525 break; 526 527 case Tattach: 528 if(error){ 529 if(r->fid) 530 closefid(removefid(srv->fpool, r->fid->fid)); 531 break; 532 } 533 break; 534 535 case Tflush: 536 assert(error == nil); 537 if(r->oldreq){ 538 if(or = removereq(r->pool, r->oldreq->tag)){ 539 assert(or == r->oldreq); 540 if(or->ifcall.type == Twalk && or->ifcall.fid != or->ifcall.newfid) 541 closefid(removefid(srv->fpool, or->newfid->fid)); 542 closereq(or); 543 } 544 closereq(r->oldreq); 545 } 546 r->oldreq = nil; 547 break; 548 549 case Twalk: 550 if(error || r->ofcall.nwqid < r->ifcall.nwname){ 551 if(r->ifcall.fid != r->ifcall.newfid && r->newfid) 552 closefid(removefid(srv->fpool, r->newfid->fid)); 553 if (r->ofcall.nwqid==0){ 554 if(error==nil && r->ifcall.nwname!=0) 555 error = Enotfound; 556 }else 557 error = nil; // No error on partial walks 558 break; 559 }else{ 560 if(r->ofcall.nwqid == 0){ 561 /* Just a clone */ 562 r->newfid->qid = r->fid->qid; 563 }else{ 564 /* if file trees are in use, filewalk took care of the rest */ 565 r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1]; 566 } 567 } 568 break; 569 570 case Topen: 571 if(error){ 572 break; 573 } 574 if(chatty9p){ 575 snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode); 576 write(2, errbuf, strlen(errbuf)); 577 } 578 r->fid->omode = r->ifcall.mode; 579 r->fid->qid = r->ofcall.qid; 580 if(r->ofcall.qid.type&QTDIR) 581 r->fid->diroffset = 0; 582 break; 583 584 case Tcreate: 585 if(error){ 586 break; 587 } 588 r->fid->omode = r->ifcall.mode; 589 r->fid->qid = r->ofcall.qid; 590 break; 591 592 case Tread: 593 if(error==nil && (r->fid->qid.type&QTDIR)) 594 r->fid->diroffset += r->ofcall.count; 595 break; 596 597 case Twrite: 598 if(error) 599 break; 600 if(r->fid->file) 601 r->fid->file->qid.vers++; 602 break; 603 604 case Tclunk: 605 break; 606 607 case Tremove: 608 if(error) 609 break; 610 if(r->fid->file){ 611 if(removefile(r->fid->file) < 0){ 612 snprint(errbuf, ERRMAX, "remove %s: %r", 613 r->fid->file->name); 614 error = errbuf; 615 } 616 r->fid->file = nil; 617 } 618 break; 619 620 case Tstat: 621 if(error) 622 break; 623 if(convD2M(&r->d, tmp, BIT16SZ) != BIT16SZ){ 624 error = "convD2M(_,_,BIT16SZ) did not return BIT16SZ"; 625 break; 626 } 627 n = GBIT16(tmp)+BIT16SZ; 628 statbuf = emalloc9p(n); 629 if(statbuf == nil){ 630 error = "out of memory"; 631 break; 632 } 633 r->ofcall.nstat = convD2M(&r->d, statbuf, n); 634 r->ofcall.stat = statbuf; 635 if(r->ofcall.nstat < 0){ 636 error = "convD2M fails"; 637 break; 638 } 639 break; 640 641 case Twstat: 642 break; 643 } 644 645 r->ofcall.tag = r->ifcall.tag; 646 r->ofcall.type = r->ifcall.type+1; 647 if(error) 648 setfcallerror(&r->ofcall, error); 649 650 if(chatty9p) 651 fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall); 652 653 qlock(&srv->wlock); 654 n = convS2M(&r->ofcall, srv->wbuf, srv->msize); 655 if(n <= 0){ 656 fprint(2, "n = %d %F\n", n, &r->ofcall); 657 abort(); 658 } 659 assert(n > 2); 660 if(r->pool){ /* not a fake */ 661 closereq(removereq(r->pool, r->ifcall.tag)); 662 closereq(r); 663 }else 664 free(r); 665 m = write(srv->outfd, srv->wbuf, n); 666 if(m != n) 667 sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd); 668 qunlock(&srv->wlock); 669 670 } 671 672 int 673 postfd(char *name, int pfd) 674 { 675 int fd; 676 char buf[80]; 677 678 snprint(buf, sizeof buf, "/srv/%s", name); 679 if(chatty9p) 680 fprint(2, "postfd %s\n", buf); 681 fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600); 682 if(fd < 0){ 683 if(chatty9p) 684 fprint(2, "create fails: %r\n"); 685 return -1; 686 } 687 if(fprint(fd, "%d", pfd) < 0){ 688 if(chatty9p) 689 fprint(2, "write fails: %r\n"); 690 close(fd); 691 return -1; 692 } 693 if(chatty9p) 694 fprint(2, "postfd successful\n"); 695 return 0; 696 } 697 698