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 = allocfid(srv->fpool, r->ifcall.fid)) == 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 492 void 493 respond(Req *r, char *error) 494 { 495 int m, n; 496 Req *or; 497 uchar *statbuf, tmp[BIT16SZ]; 498 char errbuf[ERRMAX]; 499 Srv *srv; 500 501 srv = r->srv; 502 assert(srv != nil); 503 504 assert(r->responded == 0); 505 r->responded = 1; 506 507 switch(r->ifcall.type){ 508 default: 509 assert(0); 510 511 case Tversion: 512 assert(error == nil); 513 changemsize(srv, r->ofcall.msize); 514 break; 515 516 case Tauth: 517 if(error){ 518 if(r->afid) 519 closefid(removefid(srv->fpool, r->afid->fid)); 520 break; 521 } 522 break; 523 524 case Tattach: 525 if(error){ 526 if(r->fid) 527 closefid(removefid(srv->fpool, r->fid->fid)); 528 break; 529 } 530 break; 531 532 case Tflush: 533 assert(error == nil); 534 if(r->oldreq){ 535 if(or = removereq(r->pool, r->oldreq->tag)){ 536 assert(or == r->oldreq); 537 if(or->ifcall.type == Twalk && or->ifcall.fid != or->ifcall.newfid) 538 closefid(removefid(srv->fpool, or->newfid->fid)); 539 closereq(or); 540 } 541 closereq(r->oldreq); 542 } 543 r->oldreq = nil; 544 break; 545 546 case Twalk: 547 if(error || r->ofcall.nwqid < r->ifcall.nwname){ 548 if(r->ifcall.fid != r->ifcall.newfid && r->newfid) 549 closefid(removefid(srv->fpool, r->newfid->fid)); 550 if (r->ofcall.nwqid==0){ 551 if(error==nil && r->ifcall.nwname!=0) 552 error = Enotfound; 553 }else 554 error = nil; // No error on partial walks 555 break; 556 }else{ 557 if(r->ofcall.nwqid == 0){ 558 /* Just a clone */ 559 r->newfid->qid = r->fid->qid; 560 }else{ 561 /* if file trees are in use, filewalk took care of the rest */ 562 r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1]; 563 } 564 } 565 break; 566 567 case Topen: 568 if(error){ 569 break; 570 } 571 if(chatty9p){ 572 snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode); 573 write(2, errbuf, strlen(errbuf)); 574 } 575 r->fid->omode = r->ifcall.mode; 576 r->fid->qid = r->ofcall.qid; 577 if(r->ofcall.qid.type&QTDIR) 578 r->fid->diroffset = 0; 579 break; 580 581 case Tcreate: 582 if(error){ 583 break; 584 } 585 r->fid->omode = r->ifcall.mode; 586 r->fid->qid = r->ofcall.qid; 587 break; 588 589 case Tread: 590 if(error==nil && (r->fid->qid.type&QTDIR)) 591 r->fid->diroffset += r->ofcall.count; 592 break; 593 594 case Twrite: 595 if(error) 596 break; 597 if(r->fid->file) 598 r->fid->file->qid.vers++; 599 break; 600 601 case Tclunk: 602 break; 603 604 case Tremove: 605 if(error) 606 break; 607 if(r->fid->file){ 608 if(removefile(r->fid->file) < 0){ 609 snprint(errbuf, ERRMAX, "remove %s: %r", 610 r->fid->file->name); 611 error = errbuf; 612 } 613 r->fid->file = nil; 614 } 615 break; 616 617 case Tstat: 618 if(error) 619 break; 620 if(convD2M(&r->d, tmp, BIT16SZ) != BIT16SZ){ 621 error = "convD2M(_,_,BIT16SZ) did not return BIT16SZ"; 622 break; 623 } 624 n = GBIT16(tmp)+BIT16SZ; 625 statbuf = emalloc9p(n); 626 if(statbuf == nil){ 627 error = "out of memory"; 628 break; 629 } 630 r->ofcall.nstat = convD2M(&r->d, statbuf, n); 631 r->ofcall.stat = statbuf; 632 if(r->ofcall.nstat < 0){ 633 error = "convD2M fails"; 634 break; 635 } 636 break; 637 638 case Twstat: 639 break; 640 } 641 642 r->ofcall.tag = r->ifcall.tag; 643 r->ofcall.type = r->ifcall.type+1; 644 if(error) 645 setfcallerror(&r->ofcall, error); 646 647 if(chatty9p) 648 fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall); 649 650 qlock(&srv->wlock); 651 n = convS2M(&r->ofcall, srv->wbuf, srv->msize); 652 if(n <= 0){ 653 fprint(2, "n = %d %F\n", n, &r->ofcall); 654 abort(); 655 } 656 assert(n > 2); 657 m = write(srv->outfd, srv->wbuf, n); 658 if(m != n) 659 sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd); 660 qunlock(&srv->wlock); 661 662 if(r->pool){ /* not a fake */ 663 closereq(removereq(r->pool, r->ifcall.tag)); 664 closereq(r); 665 }else 666 free(r); 667 } 668 669 int 670 postfd(char *name, int pfd) 671 { 672 int fd; 673 char buf[80]; 674 675 snprint(buf, sizeof buf, "/srv/%s", name); 676 if(chatty9p) 677 fprint(2, "postfd %s\n", buf); 678 fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600); 679 if(fd < 0){ 680 if(chatty9p) 681 fprint(2, "create fails: %r\n"); 682 return -1; 683 } 684 if(fprint(fd, "%d", pfd) < 0){ 685 if(chatty9p) 686 fprint(2, "write fails: %r\n"); 687 close(fd); 688 return -1; 689 } 690 if(chatty9p) 691 fprint(2, "postfd successful\n"); 692 return 0; 693 } 694 695