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