/sys/src/cmd/disk/kfs/9p2.c
C | 1865 lines | 1531 code | 169 blank | 165 comment | 569 complexity | 59a30f288d69447311c36b87248a9649 MD5 | raw file
1#include "all.h" 2 3#define MSIZE (MAXDAT+128) 4 5static void 6seterror(Fcall *ou, int err) 7{ 8 9 if(0 <= err && err < MAXERR) 10 ou->ename = errstring[err]; 11 else 12 ou->ename = "unknown error"; 13} 14 15static int 16fsversion(Chan* chan, Fcall* f, Fcall* r) 17{ 18 if(f->msize < MSIZE) 19 r->msize = f->msize; 20 else 21 r->msize = MSIZE; 22 /* 23 * Should check the '.' stuff here. 24 * What happens if Tversion has already been seen? 25 */ 26 if(strcmp(f->version, VERSION9P) == 0){ 27 r->version = VERSION9P; 28 chan->msize = r->msize; 29 }else 30 r->version = "unknown"; 31 32 fileinit(chan); 33 return 0; 34} 35 36char *keyspec = "proto=p9any role=server"; 37 38static int 39fsauth(Chan *chan, Fcall *f, Fcall *r) 40{ 41 int err, fd; 42 char *aname; 43 File *file; 44 int afd; 45 AuthRpc *rpc; 46 47 err = 0; 48 if(chan == cons.srvchan) 49 return Eauthmsg; 50 file = filep(chan, f->afid, 1); 51 if(file == nil) 52 return Efidinuse; 53 54 /* forget any previous authentication */ 55 file->cuid = 0; 56 57 if(access("/mnt/factotum", 0) < 0) 58 if((fd = open("/srv/factotum", ORDWR)) >= 0) 59 mount(fd, -1, "/mnt", MBEFORE, ""); 60 61 afd = open("/mnt/factotum/rpc", ORDWR); 62 if(afd < 0){ 63 err = Esystem; 64 goto out; 65 } 66 rpc = auth_allocrpc(afd); 67 if(rpc == nil){ 68 close(afd); 69 err = Esystem; 70 goto out; 71 } 72 file->rpc = rpc; 73 if(auth_rpc(rpc, "start", keyspec, strlen(keyspec)) != ARok){ 74 err = Esystem; 75 goto out; 76 } 77 78 aname = f->aname; 79 if(!aname[0]) 80 aname = "main"; 81 file->fs = fsstr(aname); 82 if(file->fs == nil){ 83 err = Ebadspc; 84 goto out; 85 } 86 file->uid = strtouid(f->uname); 87 if(file->uid < 0){ 88 err = Ebadu; 89 goto out; 90 } 91 file->qid.path = 0; 92 file->qid.vers = 0; 93 file->qid.type = QTAUTH; 94 r->qid = file->qid; 95 96out: 97 if(file != nil){ 98 qunlock(file); 99 if(err != 0) 100 freefp(file); 101 } 102 return err; 103} 104 105int 106authread(File *file, uchar *data, int count) 107{ 108 AuthInfo *ai; 109 AuthRpc *rpc; 110 int rv; 111 112 rpc = file->rpc; 113 if(rpc == nil) 114 return -1; 115 116 rv = auth_rpc(rpc, "read", nil, 0); 117 switch(rv){ 118 case ARdone: 119 ai = auth_getinfo(rpc); 120 if(ai == nil) 121 return -1; 122 if(chat) 123 print("authread identifies user as %s\n", ai->cuid); 124 file->cuid = strtouid(ai->cuid); 125 auth_freeAI(ai); 126 if(file->cuid == 0) 127 return -1; 128 if(chat) 129 print("%s is a known user\n", ai->cuid); 130 return 0; 131 case ARok: 132 if(count < rpc->narg) 133 return -1; 134 memmove(data, rpc->arg, rpc->narg); 135 return rpc->narg; 136 case ARphase: 137 return -1; 138 default: 139 return -1; 140 } 141} 142 143int 144authwrite(File *file, uchar *data, int count) 145{ 146 int ret; 147 148 ret = auth_rpc(file->rpc, "write", data, count); 149 if(ret != ARok) 150 return -1; 151 return count; 152} 153 154void 155mkqid9p1(Qid9p1* qid9p1, Qid* qid) 156{ 157 if(qid->path & 0xFFFFFFFF00000000LL) 158 panic("mkqid9p1: path %lluX\n", qid->path); 159 qid9p1->path = qid->path & 0xFFFFFFFF; 160 if(qid->type & QTDIR) 161 qid9p1->path |= QPDIR; 162 qid9p1->version = qid->vers; 163} 164 165void 166authfree(File *fp) 167{ 168 if(fp->rpc != nil){ 169 close(fp->rpc->afd); 170 free(fp->rpc); 171 fp->rpc = nil; 172 } 173} 174 175void 176mkqid9p2(Qid* qid, Qid9p1* qid9p1, int mode) 177{ 178 qid->path = (ulong)(qid9p1->path & ~QPDIR); 179 qid->vers = qid9p1->version; 180 qid->type = 0; 181 if(mode & DDIR) 182 qid->type |= QTDIR; 183 if(mode & DAPND) 184 qid->type |= QTAPPEND; 185 if(mode & DLOCK) 186 qid->type |= QTEXCL; 187} 188 189static int 190checkattach(Chan *chan, File *afile, File *file, Filsys *fs) 191{ 192 uchar buf[1]; 193 194 if(chan == cons.srvchan || chan == cons.chan) 195 return 0; 196 197 /* if no afile, this had better be none */ 198 if(afile == nil){ 199 if(file->uid == 0){ 200 if(!allownone && !chan->authed) 201 return Eauth; 202 return 0; 203 } 204 return Eauth; 205 } 206 207 /* otherwise, we'ld better have a usable cuid */ 208 if(!(afile->qid.type&QTAUTH)) 209 return Eauth; 210 if(afile->uid != file->uid || afile->fs != fs) 211 return Eauth; 212 if(afile->cuid <= 0){ 213 if(authread(afile, buf, 0) != 0) 214 return Eauth; 215 if(afile->cuid <= 0) 216 return Eauth; 217 } 218 file->uid = afile->cuid; 219 220 /* once someone has authenticated on the channel, others can become none */ 221 chan->authed = 1; 222 223 return 0; 224} 225 226static int 227fsattach(Chan* chan, Fcall* f, Fcall* r) 228{ 229 char *aname; 230 Iobuf *p; 231 Dentry *d; 232 File *file; 233 File *afile; 234 Filsys *fs; 235 long raddr; 236 int error, u; 237 238 aname = f->aname; 239 if(!aname[0]) /* default */ 240 aname = "main"; 241 p = nil; 242 afile = filep(chan, f->afid, 0); 243 file = filep(chan, f->fid, 1); 244 if(file == nil){ 245 error = Efidinuse; 246 goto out; 247 } 248 249 u = -1; 250 if(chan != cons.chan){ 251 if(strcmp(f->uname, "adm") == 0){ 252 error = Eauth; 253 goto out; 254 } 255 u = strtouid(f->uname); 256 if(u < 0){ 257 error = Ebadu; 258 goto out; 259 } 260 } 261 file->uid = u; 262 263 fs = fsstr(aname); 264 if(fs == nil){ 265 error = Ebadspc; 266 goto out; 267 } 268 269 if(error = checkattach(chan, afile, file, fs)) 270 goto out; 271 272 raddr = getraddr(fs->dev); 273 p = getbuf(fs->dev, raddr, Bread); 274 d = getdir(p, 0); 275 if(d == nil || checktag(p, Tdir, QPROOT) || !(d->mode & DALLOC)){ 276 error = Ealloc; 277 goto out; 278 } 279 if(iaccess(file, d, DEXEC)){ 280 error = Eaccess; 281 goto out; 282 } 283 if(file->uid == 0 && isro(fs->dev)) { 284 /* 285 * 'none' not allowed on dump 286 */ 287 error = Eaccess; 288 goto out; 289 } 290 accessdir(p, d, FREAD); 291 mkqid(&file->qid, d, 1); 292 file->fs = fs; 293 file->addr = raddr; 294 file->slot = 0; 295 file->open = 0; 296 freewp(file->wpath); 297 file->wpath = 0; 298 299 r->qid = file->qid; 300 301// if(cons.flags & attachflag) 302// print("9p2: attach %s %T to \"%s\" C%d\n", 303// chan->whoname, chan->whotime, fs->name, chan->chan); 304 305out: 306// if((cons.flags & attachflag) && error) 307// print("9p2: attach %s %T SUCK EGGS --- %s\n", 308// f->uname, time(), errstr[error]); 309 if(p != nil) 310 putbuf(p); 311 if(afile != nil) 312 qunlock(afile); 313 if(file != nil){ 314 qunlock(file); 315 if(error) 316 freefp(file); 317 } 318 319 return error; 320} 321 322static int 323fsflush(Chan* chan, Fcall*, Fcall*) 324{ 325 runlock(&chan->reflock); 326 wlock(&chan->reflock); 327 wunlock(&chan->reflock); 328 rlock(&chan->reflock); 329 330 return 0; 331} 332 333static void 334clone(File* nfile, File* file) 335{ 336 Wpath *wpath; 337 338 nfile->qid = file->qid; 339 340 lock(&wpathlock); 341 nfile->wpath = file->wpath; 342 for(wpath = nfile->wpath; wpath != nil; wpath = wpath->up) 343 wpath->refs++; 344 unlock(&wpathlock); 345 346 nfile->fs = file->fs; 347 nfile->addr = file->addr; 348 nfile->slot = file->slot; 349 nfile->uid = file->uid; 350 nfile->cuid = 0; 351 nfile->open = file->open & ~FREMOV; 352} 353 354static int 355walkname(File* file, char* wname, Qid* wqid) 356{ 357 Wpath *w; 358 Iobuf *p, *p1; 359 Dentry *d, *d1; 360 int error, slot; 361 long addr, qpath; 362 363 p = p1 = nil; 364 365 /* 366 * File must not have been opened for I/O by an open 367 * or create message and must represent a directory. 368 */ 369 if(file->open != 0){ 370 error = Emode; 371 goto out; 372 } 373 374 p = getbuf(file->fs->dev, file->addr, Bread); 375 if(p == nil || checktag(p, Tdir, QPNONE)){ 376 error = Edir1; 377 goto out; 378 } 379 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ 380 error = Ealloc; 381 goto out; 382 } 383 if(!(d->mode & DDIR)){ 384 error = Edir1; 385 goto out; 386 } 387 if(error = mkqidcmp(&file->qid, d)) 388 goto out; 389 390 /* 391 * For walked elements the implied user must 392 * have permission to search the directory. 393 */ 394 if(file->cp != cons.chan && iaccess(file, d, DEXEC)){ 395 error = Eaccess; 396 goto out; 397 } 398 accessdir(p, d, FREAD); 399 400 if(strcmp(wname, ".") == 0){ 401setdot: 402 if(wqid != nil) 403 *wqid = file->qid; 404 goto out; 405 } 406 if(strcmp(wname, "..") == 0){ 407 if(file->wpath == 0) 408 goto setdot; 409 putbuf(p); 410 p = nil; 411 addr = file->wpath->addr; 412 slot = file->wpath->slot; 413 p1 = getbuf(file->fs->dev, addr, Bread); 414 if(p1 == nil || checktag(p1, Tdir, QPNONE)){ 415 error = Edir1; 416 goto out; 417 } 418 if((d1 = getdir(p1, slot)) == nil || !(d1->mode & DALLOC)){ 419 error = Ephase; 420 goto out; 421 } 422 lock(&wpathlock); 423 file->wpath->refs--; 424 file->wpath = file->wpath->up; 425 unlock(&wpathlock); 426 goto found; 427 } 428 429 for(addr = 0; ; addr++){ 430 if(p == nil){ 431 p = getbuf(file->fs->dev, file->addr, Bread); 432 if(p == nil || checktag(p, Tdir, QPNONE)){ 433 error = Ealloc; 434 goto out; 435 } 436 d = getdir(p, file->slot); 437 if(d == nil || !(d->mode & DALLOC)){ 438 error = Ealloc; 439 goto out; 440 } 441 } 442 qpath = d->qid.path; 443 p1 = dnodebuf1(p, d, addr, 0); 444 p = nil; 445 if(p1 == nil || checktag(p1, Tdir, qpath)){ 446 error = Eentry; 447 goto out; 448 } 449 for(slot = 0; slot < DIRPERBUF; slot++){ 450 d1 = getdir(p1, slot); 451 if(!(d1->mode & DALLOC)) 452 continue; 453 if(strncmp(wname, d1->name, NAMELEN) != 0) 454 continue; 455 /* 456 * update walk path 457 */ 458 if((w = newwp()) == nil){ 459 error = Ewalk; 460 goto out; 461 } 462 w->addr = file->addr; 463 w->slot = file->slot; 464 w->up = file->wpath; 465 file->wpath = w; 466 slot += DIRPERBUF*addr; 467 goto found; 468 } 469 putbuf(p1); 470 p1 = nil; 471 } 472 473found: 474 file->addr = p1->addr; 475 mkqid(&file->qid, d1, 1); 476 putbuf(p1); 477 p1 = nil; 478 file->slot = slot; 479 if(wqid != nil) 480 *wqid = file->qid; 481 482out: 483 if(p1 != nil) 484 putbuf(p1); 485 if(p != nil) 486 putbuf(p); 487 488 return error; 489} 490 491static int 492fswalk(Chan* chan, Fcall* f, Fcall* r) 493{ 494 int error, nwname; 495 File *file, *nfile, tfile; 496 497 /* 498 * The file identified by f->fid must be valid in the 499 * current session and must not have been opened for I/O 500 * by an open or create message. 501 */ 502 if((file = filep(chan, f->fid, 0)) == nil) 503 return Efid; 504 if(file->open != 0){ 505 qunlock(file); 506 return Emode; 507 } 508 509 /* 510 * If newfid is not the same as fid, allocate a new file; 511 * a side effect is checking newfid is not already in use (error); 512 * if there are no names to walk this will be equivalent to a 513 * simple 'clone' operation. 514 * Otherwise, fid and newfid are the same and if there are names 515 * to walk make a copy of 'file' to be used during the walk as 516 * 'file' must only be updated on success. 517 * Finally, it's a no-op if newfid is the same as fid and f->nwname 518 * is 0. 519 */ 520 r->nwqid = 0; 521 if(f->newfid != f->fid){ 522 if((nfile = filep(chan, f->newfid, 1)) == nil){ 523 qunlock(file); 524 return Efidinuse; 525 } 526 } 527 else if(f->nwname != 0){ 528 nfile = &tfile; 529 memset(nfile, 0, sizeof(File)); 530 nfile->cp = chan; 531 nfile->fid = ~0; 532 } 533 else{ 534 qunlock(file); 535 return 0; 536 } 537 clone(nfile, file); 538 539 /* 540 * Should check name is not too long. 541 */ 542 error = 0; 543 for(nwname = 0; nwname < f->nwname; nwname++){ 544 error = walkname(nfile, f->wname[nwname], &r->wqid[r->nwqid]); 545 if(error != 0 || ++r->nwqid >= MAXDAT/sizeof(Qid)) 546 break; 547 } 548 549 if(f->nwname == 0){ 550 /* 551 * Newfid must be different to fid (see above) 552 * so this is a simple 'clone' operation - there's 553 * nothing to do except unlock unless there's 554 * an error. 555 */ 556 if(error){ 557 freewp(nfile->wpath); 558 qunlock(nfile); 559 freefp(nfile); 560 } 561 else 562 qunlock(nfile); 563 } 564 else if(r->nwqid < f->nwname){ 565 /* 566 * Didn't walk all elements, 'clunk' nfile 567 * and leave 'file' alone. 568 * Clear error if some of the elements were 569 * walked OK. 570 */ 571 freewp(nfile->wpath); 572 if(nfile != &tfile){ 573 qunlock(nfile); 574 freefp(nfile); 575 } 576 if(r->nwqid != 0) 577 error = 0; 578 } 579 else{ 580 /* 581 * Walked all elements. If newfid is the same 582 * as fid must update 'file' from the temporary 583 * copy used during the walk. 584 * Otherwise just unlock (when using tfile there's 585 * no need to unlock as it's a local). 586 */ 587 if(nfile == &tfile){ 588 file->qid = nfile->qid; 589 freewp(file->wpath); 590 file->wpath = nfile->wpath; 591 file->addr = nfile->addr; 592 file->slot = nfile->slot; 593 } 594 else 595 qunlock(nfile); 596 } 597 qunlock(file); 598 599 return error; 600} 601 602static int 603fsopen(Chan* chan, Fcall* f, Fcall* r) 604{ 605 Iobuf *p; 606 Dentry *d; 607 File *file; 608 Tlock *t; 609 Qid qid; 610 int error, ro, fmod, wok; 611 612 wok = 0; 613 p = nil; 614 615 if(chan == cons.chan || writeallow) 616 wok = 1; 617 618 if((file = filep(chan, f->fid, 0)) == nil){ 619 error = Efid; 620 goto out; 621 } 622 623 /* 624 * if remove on close, check access here 625 */ 626 ro = isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup)); 627 if(f->mode & ORCLOSE){ 628 if(ro){ 629 error = Eronly; 630 goto out; 631 } 632 /* 633 * check on parent directory of file to be deleted 634 */ 635 if(file->wpath == 0 || file->wpath->addr == file->addr){ 636 error = Ephase; 637 goto out; 638 } 639 p = getbuf(file->fs->dev, file->wpath->addr, Bread); 640 if(p == nil || checktag(p, Tdir, QPNONE)){ 641 error = Ephase; 642 goto out; 643 } 644 if((d = getdir(p, file->wpath->slot)) == nil || !(d->mode & DALLOC)){ 645 error = Ephase; 646 goto out; 647 } 648 if(iaccess(file, d, DWRITE)){ 649 error = Eaccess; 650 goto out; 651 } 652 putbuf(p); 653 } 654 p = getbuf(file->fs->dev, file->addr, Bread); 655 if(p == nil || checktag(p, Tdir, QPNONE)){ 656 error = Ealloc; 657 goto out; 658 } 659 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ 660 error = Ealloc; 661 goto out; 662 } 663 if(error = mkqidcmp(&file->qid, d)) 664 goto out; 665 mkqid(&qid, d, 1); 666 switch(f->mode & 7){ 667 668 case OREAD: 669 if(iaccess(file, d, DREAD) && !wok) 670 goto badaccess; 671 fmod = FREAD; 672 break; 673 674 case OWRITE: 675 if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok)) 676 goto badaccess; 677 if(ro){ 678 error = Eronly; 679 goto out; 680 } 681 fmod = FWRITE; 682 break; 683 684 case ORDWR: 685 if((d->mode & DDIR) 686 || (iaccess(file, d, DREAD) && !wok) 687 || (iaccess(file, d, DWRITE) && !wok)) 688 goto badaccess; 689 if(ro){ 690 error = Eronly; 691 goto out; 692 } 693 fmod = FREAD+FWRITE; 694 break; 695 696 case OEXEC: 697 if((d->mode & DDIR) || (iaccess(file, d, DEXEC) && !wok)) 698 goto badaccess; 699 fmod = FREAD; 700 break; 701 702 default: 703 error = Emode; 704 goto out; 705 } 706 if(f->mode & OTRUNC){ 707 if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok)) 708 goto badaccess; 709 if(ro){ 710 error = Eronly; 711 goto out; 712 } 713 } 714 t = 0; 715 if(d->mode & DLOCK){ 716 if((t = tlocked(p, d)) == nil){ 717 error = Elocked; 718 goto out; 719 } 720 } 721 if(f->mode & ORCLOSE) 722 fmod |= FREMOV; 723 file->open = fmod; 724 if((f->mode & OTRUNC) && !(d->mode & DAPND)){ 725 dtrunc(p, d); 726 qid.vers = d->qid.version; 727 } 728 r->qid = qid; 729 file->tlock = t; 730 if(t != nil) 731 t->file = file; 732 file->lastra = 1; 733 goto out; 734 735badaccess: 736 error = Eaccess; 737 file->open = 0; 738 739out: 740 if(p != nil) 741 putbuf(p); 742 if(file != nil) 743 qunlock(file); 744 745 r->iounit = chan->msize-IOHDRSZ; 746 747 return error; 748} 749 750static int 751dir9p2(Dir* dir, Dentry* dentry, void* strs) 752{ 753 char *op, *p; 754 755 memset(dir, 0, sizeof(Dir)); 756 mkqid(&dir->qid, dentry, 1); 757 dir->mode = (dir->qid.type<<24)|(dentry->mode & 0777); 758 dir->atime = dentry->atime; 759 dir->mtime = dentry->mtime; 760 dir->length = dentry->size; 761 762 op = p = strs; 763 dir->name = p; 764 p += sprint(p, "%s", dentry->name)+1; 765 766 dir->uid = p; 767 uidtostr(p, dentry->uid); 768 p += strlen(p)+1; 769 770 dir->gid = p; 771 uidtostr(p, dentry->gid); 772 p += strlen(p)+1; 773 774 dir->muid = p; 775 strcpy(p, ""); 776 p += strlen(p)+1; 777 778 return p-op; 779} 780 781static int 782checkname9p2(char* name) 783{ 784 char *p; 785 786 /* 787 * Return length of string if valid, 0 if not. 788 */ 789 if(name == nil) 790 return 0; 791 792 for(p = name; *p != 0; p++){ 793 if((*p & 0xFF) <= 040) 794 return 0; 795 } 796 797 return p-name; 798} 799 800static int 801fscreate(Chan* chan, Fcall* f, Fcall* r) 802{ 803 Iobuf *p, *p1; 804 Dentry *d, *d1; 805 File *file; 806 int error, slot, slot1, fmod, wok, l; 807 long addr, addr1, path; 808 Tlock *t; 809 Wpath *w; 810 811 wok = 0; 812 p = nil; 813 814 if(chan == cons.chan || writeallow) 815 wok = 1; 816 817 if((file = filep(chan, f->fid, 0)) == nil){ 818 error = Efid; 819 goto out; 820 } 821 if(isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup))){ 822 error = Eronly; 823 goto out; 824 } 825 826 p = getbuf(file->fs->dev, file->addr, Bread); 827 if(p == nil || checktag(p, Tdir, QPNONE)){ 828 error = Ealloc; 829 goto out; 830 } 831 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ 832 error = Ealloc; 833 goto out; 834 } 835 if(error = mkqidcmp(&file->qid, d)) 836 goto out; 837 if(!(d->mode & DDIR)){ 838 error = Edir2; 839 goto out; 840 } 841 if(iaccess(file, d, DWRITE) && !wok) { 842 error = Eaccess; 843 goto out; 844 } 845 accessdir(p, d, FREAD); 846 847 /* 848 * Check the name is valid and will fit in an old 849 * directory entry. 850 */ 851 if((l = checkname9p2(f->name)) == 0){ 852 error = Ename; 853 goto out; 854 } 855 if(l+1 > NAMELEN){ 856 error = Etoolong; 857 goto out; 858 } 859 if(strcmp(f->name, ".") == 0 || strcmp(f->name, "..") == 0){ 860 error = Edot; 861 goto out; 862 } 863 864 addr1 = 0; 865 slot1 = 0; /* set */ 866 for(addr = 0; ; addr++){ 867 if((p1 = dnodebuf(p, d, addr, 0)) == nil){ 868 if(addr1 != 0) 869 break; 870 p1 = dnodebuf(p, d, addr, Tdir); 871 } 872 if(p1 == nil){ 873 error = Efull; 874 goto out; 875 } 876 if(checktag(p1, Tdir, d->qid.path)){ 877 putbuf(p1); 878 goto phase; 879 } 880 for(slot = 0; slot < DIRPERBUF; slot++){ 881 d1 = getdir(p1, slot); 882 if(!(d1->mode & DALLOC)){ 883 if(addr1 == 0){ 884 addr1 = p1->addr; 885 slot1 = slot + addr*DIRPERBUF; 886 } 887 continue; 888 } 889 if(strncmp(f->name, d1->name, sizeof(d1->name)) == 0){ 890 putbuf(p1); 891 error = Eexist; 892 goto out; 893 } 894 } 895 putbuf(p1); 896 } 897 898 switch(f->mode & 7){ 899 case OEXEC: 900 case OREAD: /* seems only useful to make directories */ 901 fmod = FREAD; 902 break; 903 904 case OWRITE: 905 fmod = FWRITE; 906 break; 907 908 case ORDWR: 909 fmod = FREAD+FWRITE; 910 break; 911 912 default: 913 error = Emode; 914 goto out; 915 } 916 if(f->perm & PDIR) 917 if((f->mode & OTRUNC) || (f->perm & PAPND) || (fmod & FWRITE)) 918 goto badaccess; 919 /* 920 * do it 921 */ 922 path = qidpathgen(&file->fs->dev); 923 if((p1 = getbuf(file->fs->dev, addr1, Bread|Bimm|Bmod)) == nil) 924 goto phase; 925 d1 = getdir(p1, slot1); 926 if(d1 == nil || checktag(p1, Tdir, d->qid.path)) { 927 putbuf(p1); 928 goto phase; 929 } 930 if(d1->mode & DALLOC){ 931 putbuf(p1); 932 goto phase; 933 } 934 935 strncpy(d1->name, f->name, sizeof(d1->name)); 936 if(chan == cons.chan){ 937 d1->uid = cons.uid; 938 d1->gid = cons.gid; 939 } 940 else{ 941 d1->uid = file->uid; 942 d1->gid = d->gid; 943 f->perm &= d->mode | ~0666; 944 if(f->perm & PDIR) 945 f->perm &= d->mode | ~0777; 946 } 947 d1->qid.path = path; 948 d1->qid.version = 0; 949 d1->mode = DALLOC | (f->perm & 0777); 950 if(f->perm & PDIR) { 951 d1->mode |= DDIR; 952 d1->qid.path |= QPDIR; 953 } 954 if(f->perm & PAPND) 955 d1->mode |= DAPND; 956 t = nil; 957 if(f->perm & PLOCK){ 958 d1->mode |= DLOCK; 959 t = tlocked(p1, d1); 960 /* if nil, out of tlock structures */ 961 } 962 accessdir(p1, d1, FWRITE); 963 mkqid(&r->qid, d1, 0); 964 putbuf(p1); 965 accessdir(p, d, FWRITE); 966 967 /* 968 * do a walk to new directory entry 969 */ 970 if((w = newwp()) == nil){ 971 error = Ewalk; 972 goto out; 973 } 974 w->addr = file->addr; 975 w->slot = file->slot; 976 w->up = file->wpath; 977 file->wpath = w; 978 file->qid = r->qid; 979 file->tlock = t; 980 if(t != nil) 981 t->file = file; 982 file->lastra = 1; 983 if(f->mode & ORCLOSE) 984 fmod |= FREMOV; 985 file->open = fmod; 986 file->addr = addr1; 987 file->slot = slot1; 988 goto out; 989 990badaccess: 991 error = Eaccess; 992 goto out; 993 994phase: 995 error = Ephase; 996 997out: 998 if(p != nil) 999 putbuf(p); 1000 if(file != nil) 1001 qunlock(file); 1002 1003 r->iounit = chan->msize-IOHDRSZ; 1004 1005 return error; 1006} 1007 1008static int 1009fsread(Chan* chan, Fcall* f, Fcall* r) 1010{ 1011 uchar *data; 1012 Iobuf *p, *p1; 1013 File *file; 1014 Dentry *d, *d1; 1015 Tlock *t; 1016 long addr, offset, start, tim; 1017 int error, iounit, nread, count, n, o, slot; 1018 Dir dir; 1019 char strdata[28*10]; 1020 1021 p = nil; 1022 data = (uchar*)r->data; 1023 count = f->count; 1024 offset = f->offset; 1025 nread = 0; 1026 if((file = filep(chan, f->fid, 0)) == nil){ 1027 error = Efid; 1028 goto out; 1029 } 1030 if(file->qid.type & QTAUTH){ 1031 nread = authread(file, data, count); 1032 if(nread < 0) 1033 error = Esystem; 1034 else 1035 error = 0; 1036 goto out; 1037 } 1038 if(!(file->open & FREAD)){ 1039 error = Eopen; 1040 goto out; 1041 } 1042 iounit = chan->msize-IOHDRSZ; 1043 if(count < 0 || count > iounit){ 1044 error = Ecount; 1045 goto out; 1046 } 1047 if(offset < 0){ 1048 error = Eoffset; 1049 goto out; 1050 } 1051 p = getbuf(file->fs->dev, file->addr, Bread); 1052 if(p == nil || checktag(p, Tdir, QPNONE)){ 1053 error = Ealloc; 1054 goto out; 1055 } 1056 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ 1057 error = Ealloc; 1058 goto out; 1059 } 1060 if(error = mkqidcmp(&file->qid, d)) 1061 goto out; 1062 if(t = file->tlock){ 1063 tim = time(0); 1064 if(t->time < tim || t->file != file){ 1065 error = Ebroken; 1066 goto out; 1067 } 1068 /* renew the lock */ 1069 t->time = tim + TLOCK; 1070 } 1071 accessdir(p, d, FREAD); 1072 if(d->mode & DDIR) 1073 goto dread; 1074 if(offset+count > d->size) 1075 count = d->size - offset; 1076 while(count > 0){ 1077 if(p == nil){ 1078 p = getbuf(file->fs->dev, file->addr, Bread); 1079 if(p == nil || checktag(p, Tdir, QPNONE)){ 1080 error = Ealloc; 1081 goto out; 1082 } 1083 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ 1084 error = Ealloc; 1085 goto out; 1086 } 1087 } 1088 addr = offset / BUFSIZE; 1089 o = offset % BUFSIZE; 1090 n = BUFSIZE - o; 1091 if(n > count) 1092 n = count; 1093 p1 = dnodebuf1(p, d, addr, 0); 1094 p = nil; 1095 if(p1 != nil){ 1096 if(checktag(p1, Tfile, QPNONE)){ 1097 error = Ephase; 1098 putbuf(p1); 1099 goto out; 1100 } 1101 memmove(data+nread, p1->iobuf+o, n); 1102 putbuf(p1); 1103 } 1104 else 1105 memset(data+nread, 0, n); 1106 count -= n; 1107 nread += n; 1108 offset += n; 1109 } 1110 goto out; 1111 1112dread: 1113 /* 1114 * Pick up where we left off last time if nothing has changed, 1115 * otherwise must scan from the beginning. 1116 */ 1117 if(offset == file->doffset /*&& file->qid.vers == file->dvers*/){ 1118 addr = file->dslot/DIRPERBUF; 1119 slot = file->dslot%DIRPERBUF; 1120 start = offset; 1121 } 1122 else{ 1123 addr = 0; 1124 slot = 0; 1125 start = 0; 1126 } 1127 1128dread1: 1129 if(p == nil){ 1130 /* 1131 * This is just a check to ensure the entry hasn't 1132 * gone away during the read of each directory block. 1133 */ 1134 p = getbuf(file->fs->dev, file->addr, Bread); 1135 if(p == nil || checktag(p, Tdir, QPNONE)){ 1136 error = Ealloc; 1137 goto out1; 1138 } 1139 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ 1140 error = Ealloc; 1141 goto out1; 1142 } 1143 } 1144 p1 = dnodebuf1(p, d, addr, 0); 1145 p = nil; 1146 if(p1 == nil) 1147 goto out1; 1148 if(checktag(p1, Tdir, QPNONE)){ 1149 error = Ephase; 1150 putbuf(p1); 1151 goto out1; 1152 } 1153 1154 for(; slot < DIRPERBUF; slot++){ 1155 d1 = getdir(p1, slot); 1156 if(!(d1->mode & DALLOC)) 1157 continue; 1158 dir9p2(&dir, d1, strdata); 1159 if((n = convD2M(&dir, data+nread, iounit - nread)) <= BIT16SZ){ 1160 putbuf(p1); 1161 goto out1; 1162 } 1163 start += n; 1164 if(start < offset) 1165 continue; 1166 if(count < n){ 1167 putbuf(p1); 1168 goto out1; 1169 } 1170 count -= n; 1171 nread += n; 1172 offset += n; 1173 } 1174 putbuf(p1); 1175 slot = 0; 1176 addr++; 1177 goto dread1; 1178 1179out1: 1180 if(error == 0){ 1181 file->doffset = offset; 1182 file->dvers = file->qid.vers; 1183 file->dslot = slot+DIRPERBUF*addr; 1184 } 1185 1186out: 1187 /* 1188 * Do we need this any more? 1189 count = f->count - nread; 1190 if(count > 0) 1191 memset(data+nread, 0, count); 1192 */ 1193 if(p != nil) 1194 putbuf(p); 1195 if(file != nil) 1196 qunlock(file); 1197 r->count = nread; 1198 r->data = (char*)data; 1199 1200 return error; 1201} 1202 1203static int 1204fswrite(Chan* chan, Fcall* f, Fcall* r) 1205{ 1206 Iobuf *p, *p1; 1207 Dentry *d; 1208 File *file; 1209 Tlock *t; 1210 long offset, addr, tim, qpath; 1211 int count, error, nwrite, o, n; 1212 1213 offset = f->offset; 1214 count = f->count; 1215 1216 nwrite = 0; 1217 p = nil; 1218 1219 if((file = filep(chan, f->fid, 0)) == nil){ 1220 error = Efid; 1221 goto out; 1222 } 1223 if(file->qid.type & QTAUTH){ 1224 nwrite = authwrite(file, (uchar*)f->data, count); 1225 if(nwrite < 0) 1226 error = Esystem; 1227 else 1228 error = 0; 1229 goto out; 1230 } 1231 if(!(file->open & FWRITE)){ 1232 error = Eopen; 1233 goto out; 1234 } 1235 if(isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup))){ 1236 error = Eronly; 1237 goto out; 1238 } 1239 if(count < 0 || count > chan->msize-IOHDRSZ){ 1240 error = Ecount; 1241 goto out; 1242 } 1243 if(offset < 0) { 1244 error = Eoffset; 1245 goto out; 1246 } 1247 if((p = getbuf(file->fs->dev, file->addr, Bread|Bmod)) == nil){ 1248 error = Ealloc; 1249 goto out; 1250 } 1251 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ 1252 error = Ealloc; 1253 goto out; 1254 } 1255 if(error = mkqidcmp(&file->qid, d)) 1256 goto out; 1257 if(t = file->tlock) { 1258 tim = time(0); 1259 if(t->time < tim || t->file != file){ 1260 error = Ebroken; 1261 goto out; 1262 } 1263 /* renew the lock */ 1264 t->time = tim + TLOCK; 1265 } 1266 accessdir(p, d, FWRITE); 1267 if(d->mode & DAPND) 1268 offset = d->size; 1269 if(offset+count > d->size) 1270 d->size = offset+count; 1271 while(count > 0){ 1272 if(p == nil){ 1273 p = getbuf(file->fs->dev, file->addr, Bread|Bmod); 1274 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ 1275 error = Ealloc; 1276 goto out; 1277 } 1278 } 1279 addr = offset / BUFSIZE; 1280 o = offset % BUFSIZE; 1281 n = BUFSIZE - o; 1282 if(n > count) 1283 n = count; 1284 qpath = d->qid.path; 1285 p1 = dnodebuf1(p, d, addr, Tfile); 1286 p = nil; 1287 if(p1 == nil) { 1288 error = Efull; 1289 goto out; 1290 } 1291 if(checktag(p1, Tfile, qpath)){ 1292 putbuf(p1); 1293 error = Ephase; 1294 goto out; 1295 } 1296 memmove(p1->iobuf+o, f->data+nwrite, n); 1297 p1->flags |= Bmod; 1298 putbuf(p1); 1299 count -= n; 1300 nwrite += n; 1301 offset += n; 1302 } 1303 1304out: 1305 if(p != nil) 1306 putbuf(p); 1307 if(file != nil) 1308 qunlock(file); 1309 r->count = nwrite; 1310 1311 return error; 1312} 1313 1314static int 1315_clunk(File* file, int remove, int wok) 1316{ 1317 Tlock *t; 1318 int error; 1319 1320 error = 0; 1321 if(t = file->tlock){ 1322 if(t->file == file) 1323 t->time = 0; /* free the lock */ 1324 file->tlock = 0; 1325 } 1326 if(remove) 1327 error = doremove(file, wok); 1328 file->open = 0; 1329 freewp(file->wpath); 1330 freefp(file); 1331 qunlock(file); 1332 1333 return error; 1334} 1335 1336static int 1337fsclunk(Chan* chan, Fcall* f, Fcall*) 1338{ 1339 File *file; 1340 1341 if((file = filep(chan, f->fid, 0)) == nil) 1342 return Efid; 1343 1344 _clunk(file, file->open & FREMOV, 0); 1345 return 0; 1346} 1347 1348static int 1349fsremove(Chan* chan, Fcall* f, Fcall*) 1350{ 1351 File *file; 1352 1353 if((file = filep(chan, f->fid, 0)) == nil) 1354 return Efid; 1355 1356 return _clunk(file, 1, chan == cons.chan); 1357} 1358 1359static int 1360fsstat(Chan* chan, Fcall* f, Fcall* r, uchar* data) 1361{ 1362 Dir dir; 1363 Iobuf *p; 1364 Dentry *d; 1365 File *file; 1366 int error, len; 1367 1368 if((file = filep(chan, f->fid, 0)) == nil) 1369 return Efid; 1370 1371 p = getbuf(file->fs->dev, file->addr, Bread); 1372 if(p == nil || checktag(p, Tdir, QPNONE)){ 1373 error = Edir1; 1374 goto out; 1375 } 1376 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ 1377 error = Ealloc; 1378 goto out; 1379 } 1380 if(error = mkqidcmp(&file->qid, d)) 1381 goto out; 1382 1383 if(d->qid.path == QPROOT) /* stat of root gives time */ 1384 d->atime = time(0); 1385 1386 len = dir9p2(&dir, d, data); 1387 data += len; 1388 if((r->nstat = convD2M(&dir, data, chan->msize - len)) == 0) 1389 error = Ersc; 1390 else 1391 r->stat = data; 1392 1393out: 1394 if(p != nil) 1395 putbuf(p); 1396 if(file != nil) 1397 qunlock(file); 1398 1399 return error; 1400} 1401 1402static int 1403fswstat(Chan* chan, Fcall* f, Fcall*, char *strs) 1404{ 1405 Iobuf *p, *p1; 1406 Dentry *d, *d1, xd; 1407 File *file; 1408 int error, slot, uid, gid, l; 1409 long addr; 1410 Dir dir; 1411 ulong mode; 1412 1413 p = p1 = nil; 1414 d1 = nil; 1415 1416 if((file = filep(chan, f->fid, 0)) == nil){ 1417 error = Efid; 1418 goto out; 1419 } 1420 1421 /* 1422 * if user none, 1423 * can't do anything 1424 * unless allow. 1425 */ 1426 if(file->uid == 0 && !wstatallow){ 1427 error = Eaccess; 1428 goto out; 1429 } 1430 1431 if(isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup))){ 1432 error = Eronly; 1433 goto out; 1434 } 1435 1436 /* 1437 * first get parent 1438 */ 1439 if(file->wpath){ 1440 p1 = getbuf(file->fs->dev, file->wpath->addr, Bread); 1441 if(p1 == nil){ 1442 error = Ephase; 1443 goto out; 1444 } 1445 d1 = getdir(p1, file->wpath->slot); 1446 if(d1 == nil || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)){ 1447 error = Ephase; 1448 goto out; 1449 } 1450 } 1451 1452 if((p = getbuf(file->fs->dev, file->addr, Bread)) == nil){ 1453 error = Ealloc; 1454 goto out; 1455 } 1456 d = getdir(p, file->slot); 1457 if(d == nil || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)){ 1458 error = Ealloc; 1459 goto out; 1460 } 1461 if(error = mkqidcmp(&file->qid, d)) 1462 goto out; 1463 1464 /* 1465 * Convert the message and fix up 1466 * fields not to be changed. 1467 */ 1468 if(convM2D(f->stat, f->nstat, &dir, strs) == 0){ 1469 print("9p2: convM2D returns 0\n"); 1470 error = Econvert; 1471 goto out; 1472 } 1473 if(dir.uid == nil || strlen(dir.uid) == 0) 1474 uid = d->uid; 1475 else 1476 uid = strtouid(dir.uid); 1477 if(dir.gid == nil || strlen(dir.gid) == 0) 1478 gid = d->gid; 1479 else 1480 gid = strtouid(dir.gid); 1481 if(dir.name == nil || strlen(dir.name) == 0) 1482 dir.name = d->name; 1483 else{ 1484 if((l = checkname9p2(dir.name)) == 0){ 1485 error = Ename; 1486 goto out; 1487 } 1488 if(l > NAMELEN){ 1489 error = Etoolong; 1490 goto out; 1491 } 1492 } 1493 1494 /* 1495 * Before doing sanity checks, find out what the 1496 * new 'mode' should be: 1497 * if 'type' and 'mode' are both defaults, take the 1498 * new mode from the old directory entry; 1499 * else if 'type' is the default, use the new mode entry; 1500 * else if 'mode' is the default, create the new mode from 1501 * 'type' or'ed with the old directory mode; 1502 * else neither are defaults, use the new mode but check 1503 * it agrees with 'type'. 1504 */ 1505 if(dir.qid.type == 0xFF && dir.mode == ~0){ 1506 dir.mode = d->mode & 0777; 1507 if(d->mode & DLOCK) 1508 dir.mode |= DMEXCL; 1509 if(d->mode & DAPND) 1510 dir.mode |= DMAPPEND; 1511 if(d->mode & DDIR) 1512 dir.mode |= DMDIR; 1513 } 1514 else if(dir.qid.type == 0xFF){ 1515 /* nothing to do */ 1516 } 1517 else if(dir.mode == ~0) 1518 dir.mode = (dir.qid.type<<24)|(d->mode & 0777); 1519 else if(dir.qid.type != ((dir.mode>>24) & 0xFF)){ 1520 error = Eqidmode; 1521 goto out; 1522 } 1523 1524 /* 1525 * Check for unknown type/mode bits 1526 * and an attempt to change the directory bit. 1527 */ 1528 if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|0777)){ 1529 error = Enotm; 1530 goto out; 1531 } 1532 if(d->mode & DDIR) 1533 mode = DMDIR; 1534 else 1535 mode = 0; 1536 if((dir.mode^mode) & DMDIR){ 1537 error = Enotd; 1538 goto out; 1539 } 1540 1541 if(dir.mtime == ~0) 1542 dir.mtime = d->mtime; 1543 if(dir.length == ~0) 1544 dir.length = d->size; 1545 1546 /* 1547 * Currently, can't change length. 1548 */ 1549 if(dir.length != d->size){ 1550 error = Enotl; 1551 goto out; 1552 } 1553 1554 /* 1555 * if chown, 1556 * must be god 1557 * wstatallow set to allow chown during boot 1558 */ 1559 if(uid != d->uid && !wstatallow) { 1560 error = Enotu; 1561 goto out; 1562 } 1563 1564 /* 1565 * if chgroup, 1566 * must be either 1567 * a) owner and in new group 1568 * b) leader of both groups 1569 * wstatallow and writeallow are set to allow chgrp during boot 1570 */ 1571 while(gid != d->gid) { 1572 if(wstatallow || writeallow) 1573 break; 1574 if(d->uid == file->uid && ingroup(file->uid, gid)) 1575 break; 1576 if(leadgroup(file->uid, gid)) 1577 if(leadgroup(file->uid, d->gid)) 1578 break; 1579 error = Enotg; 1580 goto out; 1581 } 1582 1583 /* 1584 * if rename, 1585 * must have write permission in parent 1586 */ 1587 while(strncmp(d->name, dir.name, sizeof(d->name)) != 0) { 1588 if(checkname(dir.name) || d1 == nil) { 1589 error = Ename; 1590 goto out; 1591 } 1592 if(strcmp(dir.name, ".") == 0 || strcmp(xd.name, "..") == 0) { 1593 error = Ename; 1594 goto out; 1595 } 1596 1597 /* 1598 * drop entry to prevent lock, then 1599 * check that destination name is unique, 1600 */ 1601 putbuf(p); 1602 for(addr = 0; ; addr++) { 1603 if((p = dnodebuf(p1, d1, addr, 0)) == nil) 1604 break; 1605 if(checktag(p, Tdir, d1->qid.path)) { 1606 putbuf(p); 1607 continue; 1608 } 1609 for(slot = 0; slot < DIRPERBUF; slot++) { 1610 d = getdir(p, slot); 1611 if(!(d->mode & DALLOC)) 1612 continue; 1613 if(strncmp(dir.name, d->name, sizeof(d->name)) == 0) { 1614 error = Eexist; 1615 goto out; 1616 } 1617 } 1618 putbuf(p); 1619 } 1620 1621 /* 1622 * reacquire entry 1623 */ 1624 if((p = getbuf(file->fs->dev, file->addr, Bread)) == nil){ 1625 error = Ephase; 1626 goto out; 1627 } 1628 d = getdir(p, file->slot); 1629 if(d == nil || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { 1630 error = Ephase; 1631 goto out; 1632 } 1633 1634 if(wstatallow || writeallow) /* set to allow rename during boot */ 1635 break; 1636 if(d1 == nil || iaccess(file, d1, DWRITE)) { 1637 error = Eaccess; 1638 goto out; 1639 } 1640 break; 1641 } 1642 1643 /* 1644 * if mode/time, either 1645 * a) owner 1646 * b) leader of either group 1647 */ 1648 mode = dir.mode & 0777; 1649 if(dir.mode & DMAPPEND) 1650 mode |= DAPND; 1651 if(dir.mode & DMEXCL) 1652 mode |= DLOCK; 1653 while(d->mtime != dir.mtime || ((d->mode^mode) & (DAPND|DLOCK|0777))) { 1654 if(wstatallow) /* set to allow chmod during boot */ 1655 break; 1656 if(d->uid == file->uid) 1657 break; 1658 if(leadgroup(file->uid, gid)) 1659 break; 1660 if(leadgroup(file->uid, d->gid)) 1661 break; 1662 error = Enotu; 1663 goto out; 1664 } 1665 d->mtime = dir.mtime; 1666 d->uid = uid; 1667 d->gid = gid; 1668 d->mode = (mode & (DAPND|DLOCK|0777)) | (d->mode & (DALLOC|DDIR)); 1669 1670 strncpy(d->name, dir.name, sizeof(d->name)); 1671 accessdir(p, d, FWSTAT); 1672 1673out: 1674 if(p != nil) 1675 putbuf(p); 1676 if(p1 != nil) 1677 putbuf(p1); 1678 if(file != nil) 1679 qunlock(file); 1680 1681 return error; 1682} 1683 1684static int 1685recv(Chan *c, uchar *buf, int n) 1686{ 1687 int fd, m, len; 1688 1689 fd = c->chan; 1690 /* read count */ 1691 qlock(&c->rlock); 1692 m = readn(fd, buf, BIT32SZ); 1693 if(m != BIT32SZ){ 1694 qunlock(&c->rlock); 1695 if(m < 0){ 1696 print("readn(BIT32SZ) fails: %r\n"); 1697 return -1; 1698 } 1699 print("readn(BIT32SZ) returns %d: %r\n", m); 1700 return 0; 1701 } 1702 1703 len = GBIT32(buf); 1704 if(len <= BIT32SZ || len > n){ 1705 print("recv bad length %d\n", len); 1706 werrstr("bad length in 9P2000 message header"); 1707 qunlock(&c->rlock); 1708 return -1; 1709 } 1710 len -= BIT32SZ; 1711 m = readn(fd, buf+BIT32SZ, len); 1712 qunlock(&c->rlock); 1713 if(m < len){ 1714 print("recv wanted %d got %d\n", len, m); 1715 return 0; 1716 } 1717 return BIT32SZ+m; 1718} 1719 1720static void 1721send(Chan *c, uchar *buf, int n) 1722{ 1723 int fd, m; 1724 1725 fd = c->chan; 1726 qlock(&c->wlock); 1727 m = write(fd, buf, n); 1728 qunlock(&c->wlock); 1729 if(m == n) 1730 return; 1731 panic("write failed"); 1732} 1733 1734void 1735serve9p2(Chan *chan, uchar *ib, int nib) 1736{ 1737 uchar inbuf[MSIZE+IOHDRSZ], outbuf[MSIZE+IOHDRSZ]; 1738 Fcall f, r; 1739 char ename[64]; 1740 int error, n, type; 1741 1742 chan->msize = MSIZE; 1743 fmtinstall('F', fcallfmt); 1744 1745 for(;;){ 1746 if(nib){ 1747 memmove(inbuf, ib, nib); 1748 n = nib; 1749 nib = 0; 1750 }else 1751 n = recv(chan, inbuf, sizeof inbuf); 1752 if(chat){ 1753 print("read msg %d (fd %d)\n", n, chan->chan); 1754 if(n <= 0) 1755 print("\terr: %r\n"); 1756 } 1757 if(n == 0 && (chan == cons.srvchan || chan == cons.chan)) 1758 continue; 1759 if(n <= 0) 1760 break; 1761 if(convM2S(inbuf, n, &f) != n){ 1762 print("9p2: cannot decode\n"); 1763 continue; 1764 } 1765 1766 type = f.type; 1767 if(type < Tversion || type >= Tmax || (type&1) || type == Terror){ 1768 print("9p2: bad message type %d\n", type); 1769 continue; 1770 } 1771 1772 if(CHAT(chan)) 1773 print("9p2: f %F\n", &f); 1774 1775 r.type = type+1; 1776 r.tag = f.tag; 1777 error = 0; 1778 1779 rlock(&mainlock); 1780 rlock(&chan->reflock); 1781 switch(type){ 1782 default: 1783 r.type = Rerror; 1784 snprint(ename, sizeof ename, "unknown message: %F", &f); 1785 r.ename = ename; 1786 break; 1787 case Tversion: 1788 error = fsversion(chan, &f, &r); 1789 break; 1790 case Tauth: 1791 error = fsauth(chan, &f, &r); 1792 break; 1793 case Tattach: 1794 error = fsattach(chan, &f, &r); 1795 break; 1796 case Tflush: 1797 error = fsflush(chan, &f, &r); 1798 break; 1799 case Twalk: 1800 error = fswalk(chan, &f, &r); 1801 break; 1802 case Topen: 1803 error = fsopen(chan, &f, &r); 1804 break; 1805 case Tcreate: 1806 error = fscreate(chan, &f, &r); 1807 break; 1808 case Tread: 1809 r.data = (char*)inbuf; 1810 error = fsread(chan, &f, &r); 1811 break; 1812 case Twrite: 1813 error = fswrite(chan, &f, &r); 1814 break; 1815 case Tclunk: 1816 error = fsclunk(chan, &f, &r); 1817 break; 1818 case Tremove: 1819 error = fsremove(chan, &f, &r); 1820 break; 1821 case Tstat: 1822 error = fsstat(chan, &f, &r, inbuf); 1823 break; 1824 case Twstat: 1825 error = fswstat(chan, &f, &r, (char*)outbuf); 1826 break; 1827 } 1828 runlock(&chan->reflock); 1829 runlock(&mainlock); 1830 1831 if(error != 0){ 1832 r.type = Rerror; 1833 if(error >= MAXERR){ 1834 snprint(ename, sizeof(ename), "error %d", error); 1835 r.ename = ename; 1836 } 1837 else 1838 r.ename = errstring[error]; 1839 } 1840 if(CHAT(chan)) 1841 print("9p2: r %F\n", &r); 1842 1843 n = convS2M(&r, outbuf, sizeof outbuf); 1844 if(n == 0){ 1845 type = r.type; 1846 r.type = Rerror; 1847 snprint(ename, sizeof(ename), "9p2: convS2M: type %d", type); 1848 r.ename = ename; 1849 print(ename); 1850 n = convS2M(&r, outbuf, sizeof outbuf); 1851 if(n == 0){ 1852 /* 1853 * What to do here, the failure notification failed? 1854 */ 1855 panic("can't write anything at all"); 1856 } 1857 } 1858 send(chan, outbuf, n); 1859 } 1860 fileinit(chan); 1861 close(chan->chan); 1862 if(chan == cons.srvchan || chan == cons.chan) 1863 print("console chan read error"); 1864} 1865