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 #include "impl.h" 8 9 enum { 10 Tclwalk_walk = Tmax, 11 Tclwalk_clone 12 }; 13 14 static char Enowrite[] = "write prohibited"; 15 static char Enowstat[] = "wstat prohibited"; 16 static char Enoremove[] = "remove prohibited"; 17 static char Enocreate[] = "create prohibited"; 18 static char Ebotch[] = "9P protocol botch"; 19 static char Ebadattach[] = "unknown specifier in attach"; 20 21 int lib9p_chatty; 22 void (*endsrv)(void*); 23 static void 24 setfcallerror(Fcall *f, char *err) 25 { 26 strncpy(f->ename, err, sizeof f->ename); 27 f->ename[sizeof f->ename - 1] = '\0'; 28 f->type = Rerror; 29 } 30 31 static void 32 sendreply(Req *r, int fd) 33 { 34 int n; 35 char buf[MAXMSG+MAXFDATA]; 36 37 r->type++; 38 r->fcall.type = r->type; 39 if(r->error) 40 setfcallerror(&r->fcall, r->error); 41 42 if(lib9p_chatty) 43 fprint(2, "-> %F\n", &r->fcall); 44 switch(r->type){ 45 default: 46 sysfatal("bad type %d in reply", r->fcall.type); 47 break; 48 49 case Rnop: 50 case Rflush: 51 case Rattach: 52 case Rsession: 53 case Rclone: 54 case Rwalk: 55 case Rclwalk: 56 case Ropen: 57 case Rcreate: 58 case Rread: 59 case Rwrite: 60 case Rclunk: 61 case Rremove: 62 case Rstat: 63 case Rwstat: 64 case Rerror: 65 n = convS2M(&r->fcall, buf); 66 write(fd, buf, n); 67 68 } 69 } 70 71 static char Eduptag[] = "duplicate tag"; 72 static char Edupfid[] = "duplicate fid"; 73 static char Eunknownfid[] = "unknown fid"; 74 static char Eperm[] = "permission denied"; 75 static char Enotfound[] = "file not found"; 76 77 Req* 78 getreq(Reqpool *rpool, Fidpool *fpool, int fd) 79 { 80 long n; 81 char *buf; 82 Fcall f; 83 Req *r; 84 85 n = MAXMSG+MAXFDATA; 86 buf = mallocz(n, 1); 87 if(buf == nil) 88 return nil; 89 90 if(getS(fd, buf, &f, &n) != nil){ 91 free(buf); 92 return nil; 93 } 94 if((r=allocreq(rpool, f.tag)) == nil){ /* duplicate tag: cons up a Req */ 95 r = mallocz(sizeof *r, 1); 96 if(r == nil){ 97 free(buf); 98 return nil; 99 } 100 incref(&r->ref); 101 r->tag = f.tag; 102 r->fcall = f; 103 r->error = Eduptag; 104 r->buf = buf; 105 r->responded = 0; 106 return r; 107 } 108 109 r->responded = 0; 110 r->buf = buf; 111 r->fcall = f; 112 r->ofcall = f; 113 r->type = r->fcall.type; 114 switch(r->type){ 115 case Tnop: 116 case Tsession: 117 break; 118 119 case Tflush: 120 r->oldreq = lookupreq(rpool, r->fcall.oldtag); 121 if(r->oldreq == r) 122 r->error = "you can't flush yourself"; 123 break; 124 125 case Tattach: 126 if((r->fid = allocfid(fpool, r->fcall.fid)) == nil) 127 r->error = Edupfid; 128 break; 129 130 case Tclone: 131 case Tclwalk: 132 if((r->fid = lookupfid(fpool, r->fcall.fid)) == nil) 133 r->error = Eunknownfid; 134 else if(r->fid->omode != -1) 135 r->error = "cannot clone open fid"; 136 else if((r->newfid = allocfid(fpool, r->fcall.newfid)) == nil) 137 r->error = Edupfid; 138 break; 139 140 case Twalk: 141 case Topen: 142 case Tcreate: 143 case Tread: 144 case Twrite: 145 case Tclunk: 146 case Tremove: 147 case Tstat: 148 case Twstat: 149 if((r->fid = lookupfid(fpool, r->fcall.fid)) == nil) 150 r->error = Eunknownfid; 151 break; 152 } 153 if(lib9p_chatty) 154 if(r->error) 155 fprint(2, "<- %F: %s\n", &r->fcall, r->error); 156 else 157 fprint(2, "<- %F\n", &r->fcall); 158 159 return r; 160 } 161 162 163 static char Ebadcount[] = "bad count"; 164 static char Ebadoffset[] = "bad offset"; 165 166 void 167 srv(Srv *srv, int fd) 168 { 169 int o, p; 170 Req *r; 171 File *f, *of; 172 173 fmtinstall('D', dirconv); 174 fmtinstall('F', fcallconv); 175 176 if(srv->fpool == nil) 177 srv->fpool = allocfidpool(); 178 if(srv->rpool == nil) 179 srv->rpool = allocreqpool(); 180 181 srv->rpool->srv = srv; 182 srv->fd = fd; 183 184 while(r = getreq(srv->rpool, srv->fpool, fd)){ 185 if(r->error){ 186 respond(r, r->error); 187 continue; 188 } 189 190 switch(r->type){ 191 default: 192 abort(); 193 194 case Tnop: 195 respond(r, nil); 196 break; 197 198 case Tsession: 199 memset(r->fcall.authid, 0, sizeof r->fcall.authid); 200 memset(r->fcall.authdom, 0, sizeof r->fcall.authdom); 201 memset(r->fcall.chal, 0, sizeof r->fcall.chal); 202 if(srv->session) 203 srv->session(r, r->fcall.authid, 204 r->fcall.authdom, r->fcall.chal); 205 else 206 respond(r, nil); 207 break; 208 209 case Tflush: 210 if(r->oldreq && srv->flush) 211 srv->flush(r, r->oldreq); 212 else 213 respond(r, nil); 214 break; 215 216 case Tattach: 217 strncpy(r->fid->uid, r->ofcall.uname, NAMELEN); 218 r->fid->uid[NAMELEN-1] = '\0'; 219 if(srv->attach) 220 srv->attach(r, r->fid, r->ofcall.aname, &r->fcall.qid); 221 else{ /* assume one tree, no auth */ 222 if(strcmp(r->ofcall.aname, "") != 0) 223 r->error = Ebadattach; 224 else{ 225 r->fid->file = srv->tree->root; 226 r->fcall.qid = r->fid->file->qid; 227 } 228 respond(r, nil); 229 } 230 break; 231 232 case Tclwalk: 233 r->type = Tclwalk_clone; 234 /* fall through */ 235 case Tclone: 236 if(r->fid->file) 237 incref(&r->fid->file->ref); 238 r->newfid->file = r->fid->file; 239 240 strcpy(r->newfid->uid, r->fid->uid); 241 r->newfid->qid = r->fid->qid; 242 r->newfid->aux = r->fid->aux; 243 244 if(srv->clone) 245 srv->clone(r, r->fid, r->newfid); 246 else 247 respond(r, nil); 248 break; 249 250 case Twalk: 251 /* if you change this, change the copy in respond Tclwalk_clone above */ 252 253 if(r->fid->file){ 254 /* 255 * We don't use the walk function if using file 256 * trees. It's too much to expect clients to get 257 * the reference counts right on error or when 258 * playing other funny games with the tree. 259 */ 260 if(f = fwalk(r->fid->file, r->ofcall.name)) { 261 of = r->fid->file; 262 r->fid->file = f; 263 fclose(of); 264 r->fcall.qid = f->qid; 265 respond(r, nil); 266 }else 267 respond(r, Enotfound); 268 break; 269 } 270 r->fcall.qid = r->fid->qid; 271 srv->walk(r, r->fid, r->ofcall.name, &r->fcall.qid); 272 break; 273 274 case Topen: 275 if(r->fid->omode != -1){ 276 respond(r, Ebotch); 277 break; 278 } 279 if(r->fid->file){ 280 switch(r->fcall.mode&3){ 281 case OREAD: p = AREAD; break; 282 case OWRITE: p = AWRITE; break; 283 case ORDWR: p = AREAD|AWRITE; break; 284 case OEXEC: p = AEXEC; break; 285 default: p = ~0; assert(0); 286 } 287 if(r->fcall.mode & OTRUNC) 288 p |= AWRITE; 289 if(!hasperm(r->fid->file, r->fid->uid, p)){ 290 respond(r, Eperm); 291 break; 292 } 293 if((r->fcall.mode & ORCLOSE) 294 && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){ 295 respond(r, Eperm); 296 break; 297 } 298 r->fcall.qid = r->fid->file->qid; 299 } 300 if(srv->open) 301 srv->open(r, r->fid, r->fcall.mode, &r->fcall.qid); 302 else 303 respond(r, nil); 304 break; 305 306 case Tcreate: 307 if(r->fid->omode != -1) 308 r->error = Ebotch; 309 else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE)) 310 r->error = Eperm; 311 else if(srv->create == nil) 312 r->error = Enocreate; 313 314 if(r->error) 315 respond(r, r->error); 316 else 317 srv->create(r, r->fid, r->ofcall.name, r->ofcall.mode, 318 r->ofcall.perm, &r->fcall.qid); 319 break; 320 321 case Tread: 322 r->rbuf = emalloc(MAXFDATA); 323 r->fcall.data = r->rbuf; 324 o = r->fid->omode & OMASK; 325 if(r->fcall.count > MAXFDATA) 326 r->fcall.count = MAXFDATA; 327 if(o != OREAD && o != ORDWR && o != OEXEC) 328 r->error = Ebotch; 329 else if(r->fcall.count < 0) 330 r->error = Ebadcount; 331 else if(r->fcall.offset < 0 332 || ((r->fid->qid.path&CHDIR) && r->fcall.offset%DIRLEN)) 333 r->error = Ebadoffset; 334 else if((r->fid->qid.path&CHDIR) && r->fid->file){ 335 r->error = fdirread(r->fid->file, r->rbuf, 336 &r->fcall.count, r->fcall.offset); 337 }else{ 338 srv->read(r, r->fid, r->rbuf, &r->fcall.count, r->fcall.offset); 339 break; 340 } 341 respond(r, r->error); 342 break; 343 344 case Twrite: 345 o = r->fid->omode & OMASK; 346 if(o != OWRITE && o != ORDWR) 347 r->error = Ebotch; 348 else if(srv->write == nil) 349 r->error = Enowrite; 350 else{ 351 srv->write(r, r->fid, r->fcall.data, 352 &r->fcall.count, r->fcall.offset); 353 break; 354 } 355 respond(r, r->error); 356 break; 357 358 case Tclunk: 359 if(srv->clunkaux) 360 srv->clunkaux(r->fid); 361 respond(r, nil); 362 break; 363 364 case Tremove: 365 if(r->fid->file 366 && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){ 367 respond(r, Eperm); 368 break; 369 } 370 if(srv->remove == nil){ 371 if(r->fid->file) 372 respond(r, nil); 373 else 374 respond(r, Enoremove); 375 break; 376 } 377 srv->remove(r, r->fid); 378 break; 379 380 case Tstat: 381 if(r->fid->file) 382 r->d = r->fid->file->Dir; 383 else if(srv->stat == nil){ 384 respond(r, "no stat"); 385 break; 386 } 387 388 if(srv->stat) 389 srv->stat(r, r->fid, &r->d); 390 else 391 respond(r, nil); 392 break; 393 394 case Twstat: 395 if(srv->wstat == nil) 396 respond(r, nil); 397 else{ 398 convM2D(r->fcall.stat, &r->d); 399 srv->wstat(r, r->fid, &r->d); 400 } 401 break; 402 } 403 } 404 } 405 406 void 407 respond(Req *r, char *error) 408 { 409 File *f, *of; 410 Srv *srv; 411 412 srv = r->pool->srv; 413 assert(r->responded == 0); 414 r->responded = 1; 415 416 switch(r->type){ 417 default: 418 abort(); 419 420 case Tnop: 421 break; 422 423 case Tflush: 424 freereq(r->oldreq); 425 r->oldreq = nil; 426 break; 427 428 case Tattach: 429 if(error) 430 freefid(r->fid); 431 else { 432 r->fid->qid = r->fcall.qid; 433 closefid(r->fid); 434 } 435 r->fid = nil; 436 break; 437 438 case Tsession: 439 break; 440 441 case Tclone: 442 closefid(r->fid); 443 r->fid = nil; 444 if(error) 445 freefid(r->newfid); 446 else 447 closefid(r->newfid); 448 r->newfid = nil; 449 break; 450 451 case Tclwalk: /* can't happen */ 452 abort(); 453 454 /* 455 * In order to use the clone and walk functions rather 456 * than require a clwalk, the response here is an error 457 * if there was one, but otherwise we call the walk 458 * function, who will eventually call us again (next case). 459 */ 460 case Tclwalk_clone: 461 closefid(r->fid); 462 r->fid = nil; 463 if(error){ 464 freefid(r->newfid); 465 r->newfid = nil; 466 break; 467 } 468 469 r->fid = r->newfid; 470 r->newfid = nil; 471 r->type = Tclwalk_walk; 472 473 /* copy of case Twalk in srv below */ 474 if(r->fid->file){ 475 /* 476 * We don't use the walk function if using file 477 * trees. It's too much to expect clients to get 478 * the reference counts right on error or when 479 * playing other funny games with the tree. 480 */ 481 if(f = fwalk(r->fid->file, r->fcall.name)) { 482 of = r->fid->file; 483 r->fid->file = f; 484 fclose(of); 485 r->fid->qid = f->qid; 486 r->fcall.qid = r->fid->qid; 487 respond(r, nil); 488 }else 489 respond(r, Enotfound); 490 break; 491 } 492 r->fcall.qid = r->fid->qid; 493 srv->walk(r, r->fid, r->ofcall.name, &r->fcall.qid); 494 break; 495 496 case Tclwalk_walk: 497 r->type = Tclwalk; 498 if(error) 499 freefid(r->fid); 500 else{ 501 r->fid->qid = r->fcall.qid; 502 closefid(r->fid); 503 } 504 r->fid = nil; 505 break; 506 507 case Twalk: 508 if(error == nil) 509 r->fid->qid = r->fcall.qid; 510 closefid(r->fid); 511 r->fid = nil; 512 break; 513 514 case Topen: 515 if(error == nil){ 516 r->fid->omode = r->fcall.mode; 517 r->fid->qid = r->fcall.qid; 518 } 519 closefid(r->fid); 520 r->fid = nil; 521 break; 522 523 case Tcreate: 524 if(error == nil){ 525 r->fid->omode = r->fcall.mode; 526 r->fid->qid = r->fcall.qid; 527 } 528 closefid(r->fid); 529 r->fid = nil; 530 break; 531 532 case Tread: 533 closefid(r->fid); 534 r->fid = nil; 535 break; 536 537 case Twrite: 538 if(r->fid->file) 539 r->fid->file->qid.vers++; 540 closefid(r->fid); 541 r->fid = nil; 542 break; 543 544 case Tclunk: 545 freefid(r->fid); 546 r->fid = nil; 547 break; 548 549 case Tremove: 550 if(error == nil) { 551 if(r->fid->file) { 552 fremove(r->fid->file); 553 r->fid->file = nil; 554 } 555 } 556 if(srv->clunkaux) 557 srv->clunkaux(r->fid); 558 freefid(r->fid); 559 r->fid = nil; 560 break; 561 562 case Tstat: 563 if(error == nil) 564 convD2M(&r->d, r->fcall.stat); 565 closefid(r->fid); 566 r->fid = nil; 567 break; 568 569 case Twstat: 570 closefid(r->fid); 571 r->fid = nil; 572 break; 573 } 574 575 assert(r->type != Tclwalk_clone); 576 577 if(r->type == Tclwalk_walk) 578 return; 579 580 r->error = error; 581 sendreply(r, r->pool->srv->fd); 582 freereq(r); 583 } 584 585 586 /* 587 * read a message from fd and convert it. 588 * ignore 0-length messages. 589 */ 590 char * 591 getS(int fd, char *buf, Fcall *f, long *lp) 592 { 593 long m, n; 594 int i; 595 char *errstr; 596 597 errstr = "EOF"; 598 n = 0; 599 for(i = 0; i < 3; i++){ 600 n = read(fd, buf, *lp); 601 if(n == 0){ 602 continue; 603 } 604 if(n < 0) 605 return "read error"; 606 m = convM2S(buf, f, n); 607 if(m == 0){ 608 errstr = "bad type"; 609 continue; 610 } 611 *lp = m; 612 return 0; 613 } 614 *lp = n; 615 return errstr; 616 } 617 618 void 619 _postfd(char *name, int pfd) 620 { 621 char buf[2*NAMELEN]; 622 int fd; 623 624 snprint(buf, sizeof buf, "/srv/%s", name); 625 626 fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600); 627 if(fd == -1) 628 sysfatal("post %s: %r", name); 629 fprint(fd, "%d", pfd); 630 } 631 632 void 633 postmountsrv(Srv *s, char *name, char *mtpt, int flag) 634 { 635 int fd[2]; 636 if(pipe(fd) < 0) 637 sysfatal("pipe"); 638 if(name) 639 _postfd(name, fd[0]); 640 switch(rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG|RFMEM)){ 641 case -1: 642 sysfatal("fork"); 643 case 0: 644 close(fd[0]); 645 srv(s, fd[1]); 646 if(endsrv) 647 endsrv(nil); 648 _exits(0); 649 default: 650 if(mtpt) 651 if(mount(fd[0], mtpt, flag, "") == -1) 652 fprint(2, "mount %s: %r\n", mtpt); 653 } 654 } 655 656