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 23 static void 24 setfcallerror(Fcall *f, char *err) 25 { 26 strncpy(f->ename, err, sizeof f->ename); 27 f->ename[sizeof f->name - 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 char buf[MAXFDATA]; 172 File *f, *of; 173 174 fmtinstall('D', dirconv); 175 fmtinstall('F', fcallconv); 176 177 if(srv->fpool == nil) 178 srv->fpool = allocfidpool(); 179 if(srv->rpool == nil) 180 srv->rpool = allocreqpool(); 181 182 srv->rpool->srv = srv; 183 srv->fd = fd; 184 185 while(r = getreq(srv->rpool, srv->fpool, fd)){ 186 if(r->error){ 187 respond(r, r->error); 188 continue; 189 } 190 191 switch(r->type){ 192 default: 193 abort(); 194 195 case Tnop: 196 respond(r, nil); 197 break; 198 199 case Tsession: 200 memset(r->fcall.authid, 0, sizeof r->fcall.authid); 201 memset(r->fcall.authdom, 0, sizeof r->fcall.authdom); 202 memset(r->fcall.chal, 0, sizeof r->fcall.chal); 203 if(srv->session) 204 srv->session(r, r->fcall.authid, 205 r->fcall.authdom, r->fcall.chal); 206 else 207 respond(r, nil); 208 break; 209 210 case Tflush: 211 if(r->oldreq && srv->flush) 212 srv->flush(r, r->oldreq); 213 else 214 respond(r, nil); 215 break; 216 217 case Tattach: 218 strncpy(r->fid->uid, r->ofcall.uname, NAMELEN); 219 r->fid->uid[NAMELEN-1] = '\0'; 220 if(srv->attach) 221 srv->attach(r, r->fid, r->ofcall.aname, &r->fcall.qid); 222 else{ /* assume one tree, no auth */ 223 if(strcmp(r->ofcall.aname, "") != 0) 224 r->error = Ebadattach; 225 else{ 226 r->fid->file = srv->tree->root; 227 r->fcall.qid = r->fid->file->qid; 228 } 229 respond(r, nil); 230 } 231 break; 232 233 case Tclwalk: 234 r->type = Tclwalk_clone; 235 /* fall through */ 236 case Tclone: 237 if(r->fid->file) 238 incref(&r->fid->file->ref); 239 r->newfid->file = r->fid->file; 240 241 strcpy(r->newfid->uid, r->fid->uid); 242 r->newfid->qid = r->fid->qid; 243 r->newfid->aux = r->fid->aux; 244 245 if(srv->clone) 246 srv->clone(r, r->fid, r->newfid); 247 else 248 respond(r, nil); 249 break; 250 251 case Twalk: 252 /* if you change this, change the copy in respond Tclwalk_clone above */ 253 254 if(r->fid->file){ 255 /* 256 * We don't use the walk function if using file 257 * trees. It's too much to expect clients to get 258 * the reference counts right on error or when 259 * playing other funny games with the tree. 260 */ 261 if(f = fwalk(r->fid->file, r->ofcall.name)) { 262 of = r->fid->file; 263 r->fid->file = f; 264 fclose(of); 265 r->fcall.qid = f->qid; 266 respond(r, nil); 267 }else 268 respond(r, Enotfound); 269 break; 270 } 271 r->fcall.qid = r->fid->qid; 272 srv->walk(r, r->fid, r->ofcall.name, &r->fcall.qid); 273 break; 274 275 case Topen: 276 if(r->fid->omode != -1){ 277 respond(r, Ebotch); 278 break; 279 } 280 if(r->fid->file){ 281 switch(r->fcall.mode&3){ 282 case OREAD: p = AREAD; break; 283 case OWRITE: p = AWRITE; break; 284 case ORDWR: p = AREAD|AWRITE; break; 285 case OEXEC: p = AEXEC; break; 286 default: p = ~0; assert(0); 287 } 288 if(r->fcall.mode & OTRUNC) 289 p |= AWRITE; 290 if(!hasperm(r->fid->file, r->fid->uid, p)){ 291 respond(r, Eperm); 292 break; 293 } 294 if((r->fcall.mode & ORCLOSE) 295 && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){ 296 respond(r, Eperm); 297 break; 298 } 299 r->fcall.qid = r->fid->file->qid; 300 } 301 if(srv->open) 302 srv->open(r, r->fid, r->fcall.mode, &r->fcall.qid); 303 else 304 respond(r, nil); 305 break; 306 307 case Tcreate: 308 if(r->fid->omode != -1) 309 r->error = Ebotch; 310 else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE)) 311 r->error = Eperm; 312 else if(srv->create == nil) 313 r->error = Enocreate; 314 315 if(r->error) 316 respond(r, r->error); 317 else 318 srv->create(r, r->fid, r->ofcall.name, r->ofcall.mode, 319 r->ofcall.perm, &r->fcall.qid); 320 break; 321 322 case Tread: 323 r->fcall.data = buf; 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, buf, 336 &r->fcall.count, r->fcall.offset); 337 }else{ 338 srv->read(r, r->fid, buf, &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, 0666); 627 if(fd == -1) 628 sysfatal("postsrv"); 629 fprint(fd, "%d", pfd); 630 close(fd); 631 } 632 633 void 634 postmountsrv(Srv *s, char *name, char *mtpt, int flag) 635 { 636 int fd[2]; 637 if(pipe(fd) < 0) 638 sysfatal("pipe"); 639 if(name) 640 _postfd(name, fd[0]); 641 switch(rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG)){ 642 case -1: 643 sysfatal("fork"); 644 case 0: 645 close(fd[0]); 646 srv(s, fd[1]); 647 _exits(0); 648 default: 649 if(mtpt) 650 mount(fd[0], mtpt, flag, ""); 651 } 652 } 653 654