1 #include "stdinc.h" 2 3 #include "9.h" 4 5 enum { 6 OMODE = 0x7, /* Topen/Tcreate mode */ 7 }; 8 9 enum { 10 PermX = 1, 11 PermW = 2, 12 PermR = 4, 13 }; 14 15 static char EPermission[] = "permission denied"; 16 17 static int 18 permFile(File* file, Fid* fid, int perm) 19 { 20 char *u; 21 DirEntry de; 22 23 if(!fileGetDir(file, &de)) 24 return 0; 25 26 /* 27 * User none only gets other permissions. 28 */ 29 if(strcmp(fid->uname, unamenone) != 0){ 30 /* 31 * There is only one uid<->uname mapping 32 * and it's already cached in the Fid, but 33 * it might have changed during the lifetime 34 * if this Fid. 35 */ 36 if((u = unameByUid(de.uid)) != nil){ 37 if(strcmp(fid->uname, u) == 0 && ((perm<<6) & de.mode)){ 38 vtMemFree(u); 39 deCleanup(&de); 40 return 1; 41 } 42 vtMemFree(u); 43 } 44 if(groupMember(de.gid, fid->uname) && ((perm<<3) & de.mode)){ 45 deCleanup(&de); 46 return 1; 47 } 48 } 49 if(perm & de.mode){ 50 if(perm == PermX && (de.mode & ModeDir)){ 51 deCleanup(&de); 52 return 1; 53 } 54 if(!groupMember(uidnoworld, fid->uname)){ 55 deCleanup(&de); 56 return 1; 57 } 58 } 59 if(fsysNoPermCheck(fid->fsys)){ 60 deCleanup(&de); 61 return 1; 62 } 63 vtSetError(EPermission); 64 65 deCleanup(&de); 66 return 0; 67 } 68 69 static int 70 permFid(Fid* fid, int p) 71 { 72 return permFile(fid->file, fid, p); 73 } 74 75 static int 76 permParent(Fid* fid, int p) 77 { 78 int r; 79 File *parent; 80 81 parent = fileGetParent(fid->file); 82 r = permFile(parent, fid, p); 83 fileDecRef(parent); 84 85 return r; 86 } 87 88 int 89 validFileName(char* name) 90 { 91 char *p; 92 93 if(name == nil || name[0] == '\0'){ 94 vtSetError("no file name"); 95 return 0; 96 } 97 if(name[0] == '.'){ 98 if(name[1] == '\0' || (name[1] == '.' && name[2] == '\0')){ 99 vtSetError(". and .. illegal as file name"); 100 return 0; 101 } 102 } 103 104 for(p = name; *p != '\0'; p++){ 105 if((*p & 0xFF) < 040){ 106 vtSetError("bad character in file name"); 107 return 0; 108 } 109 } 110 111 return 1; 112 } 113 114 static int 115 rTwstat(Msg* m) 116 { 117 Dir dir; 118 Fid *fid; 119 ulong mode; 120 DirEntry de; 121 char *gid, *strs, *uid; 122 int gl, op, retval, tsync; 123 124 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil) 125 return 0; 126 127 gid = uid = nil; 128 retval = 0; 129 130 if(strcmp(fid->uname, unamenone) == 0 || (fid->qid.type & QTAUTH)){ 131 vtSetError(EPermission); 132 goto error0; 133 } 134 if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){ 135 vtSetError("read-only filesystem"); 136 goto error0; 137 } 138 139 if(!fileGetDir(fid->file, &de)) 140 goto error0; 141 142 strs = vtMemAlloc(m->t.nstat); 143 if(convM2D(m->t.stat, m->t.nstat, &dir, strs) == 0){ 144 vtSetError("wstat -- protocol botch"); 145 goto error; 146 } 147 148 /* 149 * Run through each of the (sub-)fields in the provided Dir 150 * checking for validity and whether it's a default: 151 * .type, .dev and .atime are completely ignored and not checked; 152 * .qid.path, .qid.vers and .muid are checked for validity but 153 * any attempt to change them is an error. 154 * .qid.type/.mode, .mtime, .name, .length, .uid and .gid can 155 * possibly be changed. 156 * 157 * 'Op' flags there are changed fields, i.e. it's not a no-op. 158 * 'Tsync' flags all fields are defaulted. 159 */ 160 tsync = 1; 161 if(dir.qid.path != ~0){ 162 if(dir.qid.path != de.qid){ 163 vtSetError("wstat -- attempt to change qid.path"); 164 goto error; 165 } 166 tsync = 0; 167 } 168 if(dir.qid.vers != ~0){ 169 if(dir.qid.vers != de.mcount){ 170 vtSetError("wstat -- attempt to change qid.vers"); 171 goto error; 172 } 173 tsync = 0; 174 } 175 if(dir.muid != nil && *dir.muid != '\0'){ 176 if((uid = uidByUname(dir.muid)) == nil){ 177 vtSetError("wstat -- unknown muid"); 178 goto error; 179 } 180 if(strcmp(uid, de.mid) != 0){ 181 vtSetError("wstat -- attempt to change muid"); 182 goto error; 183 } 184 vtMemFree(uid); 185 uid = nil; 186 tsync = 0; 187 } 188 189 /* 190 * Check .qid.type and .mode agree if neither is defaulted. 191 */ 192 if(dir.qid.type != (uchar)~0 && dir.mode != ~0){ 193 if(dir.qid.type != ((dir.mode>>24) & 0xFF)){ 194 vtSetError("wstat -- qid.type/mode mismatch"); 195 goto error; 196 } 197 } 198 199 op = 0; 200 201 if(dir.qid.type != (uchar)~0 || dir.mode != ~0){ 202 /* 203 * .qid.type or .mode isn't defaulted, check for unknown bits. 204 */ 205 if(dir.mode == ~0) 206 dir.mode = (dir.qid.type<<24)|(de.mode & 0777); 207 if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|0777)){ 208 vtSetError("wstat -- unknown bits in qid.type/mode"); 209 goto error; 210 } 211 212 /* 213 * Synthesise a mode to check against the current settings. 214 */ 215 mode = dir.mode & 0777; 216 if(dir.mode & DMEXCL) 217 mode |= ModeExclusive; 218 if(dir.mode & DMAPPEND) 219 mode |= ModeAppend; 220 if(dir.mode & DMDIR) 221 mode |= ModeDir; 222 223 if((de.mode^mode) & ModeDir){ 224 vtSetError("wstat -- attempt to change directory bit"); 225 goto error; 226 } 227 228 if((de.mode & (ModeAppend|ModeExclusive|0777)) != mode){ 229 de.mode &= ~(ModeAppend|ModeExclusive|0777); 230 de.mode |= mode; 231 op = 1; 232 } 233 tsync = 0; 234 } 235 236 if(dir.mtime != ~0){ 237 if(dir.mtime != de.mtime){ 238 de.mtime = dir.mtime; 239 op = 1; 240 } 241 tsync = 0; 242 } 243 244 if(dir.length != ~0){ 245 if(de.mode & ModeDir){ 246 vtSetError("wstat -- attempt to change length of directory"); 247 goto error; 248 } 249 if(dir.length != de.size){ 250 de.size = dir.length; 251 op = 1; 252 } 253 tsync = 0; 254 } 255 256 /* 257 * Check for permission to change .mode, .mtime or .length, 258 * must be owner or leader of either group, for which test gid 259 * is needed; permission checks on gid will be done later. 260 */ 261 if(dir.gid != nil && *dir.gid != '\0'){ 262 if((gid = uidByUname(dir.gid)) == nil){ 263 vtSetError("wstat -- unknown gid"); 264 goto error; 265 } 266 tsync = 0; 267 } 268 else 269 gid = vtStrDup(de.gid); 270 271 /* 272 * 'Gl' counts whether neither, one or both groups are led. 273 */ 274 gl = groupLeader(gid, fid->uname) != 0; 275 gl += groupLeader(de.gid, fid->uname) != 0; 276 277 if(op && !fsysWstatAllow(fid->fsys)){ 278 if(strcmp(fid->uid, de.uid) != 0 && !gl){ 279 vtSetError("wstat -- not owner or group leader"); 280 goto error; 281 } 282 } 283 284 /* 285 * Check for permission to change group, must be 286 * either owner and in new group or leader of both groups. 287 * If gid is nil here then 288 */ 289 if(strcmp(gid, de.gid) != 0){ 290 if(!fsysWstatAllow(fid->fsys) 291 && !(strcmp(fid->uid, de.uid) == 0 && groupMember(gid, fid->uname)) 292 && !(gl == 2)){ 293 vtSetError("wstat -- not owner and not group leaders"); 294 goto error; 295 } 296 vtMemFree(de.gid); 297 de.gid = gid; 298 gid = nil; 299 op = 1; 300 } 301 302 /* 303 * Rename. 304 * Check .name is valid and different to the current. 305 * If so, check write permission in parent. 306 */ 307 if(dir.name != nil && *dir.name != '\0'){ 308 if(!validFileName(dir.name)) 309 goto error; 310 if(strcmp(dir.name, de.elem) != 0){ 311 if(!permParent(fid, PermW)) 312 goto error; 313 vtMemFree(de.elem); 314 de.elem = vtStrDup(dir.name); 315 op = 1; 316 } 317 tsync = 0; 318 } 319 320 /* 321 * Check for permission to change owner - must be god. 322 */ 323 if(dir.uid != nil && *dir.uid != '\0'){ 324 if((uid = uidByUname(dir.uid)) == nil){ 325 vtSetError("wstat -- unknown uid"); 326 goto error; 327 } 328 if(strcmp(uid, de.uid) != 0){ 329 if(!fsysWstatAllow(fid->fsys)){ 330 vtSetError("wstat -- not owner"); 331 goto error; 332 } 333 if(strcmp(uid, uidnoworld) == 0){ 334 vtSetError(EPermission); 335 goto error; 336 } 337 vtMemFree(de.uid); 338 de.uid = uid; 339 uid = nil; 340 op = 1; 341 } 342 tsync = 0; 343 } 344 345 if(op) 346 retval = fileSetDir(fid->file, &de, fid->uid); 347 else 348 retval = 1; 349 350 if(tsync){ 351 /* 352 * All values were defaulted, 353 * make the state of the file exactly what it 354 * claims to be before returning... 355 */ 356 USED(tsync); 357 } 358 359 error: 360 deCleanup(&de); 361 vtMemFree(strs); 362 if(gid != nil) 363 vtMemFree(gid); 364 if(uid != nil) 365 vtMemFree(uid); 366 error0: 367 fidPut(fid); 368 return retval; 369 }; 370 371 static int 372 rTstat(Msg* m) 373 { 374 Dir dir; 375 Fid *fid; 376 DirEntry de; 377 378 if((fid = fidGet(m->con, m->t.fid, 0)) == nil) 379 return 0; 380 if(fid->qid.type & QTAUTH){ 381 memset(&dir, 0, sizeof(Dir)); 382 dir.qid = fid->qid; 383 dir.mode = DMAUTH; 384 dir.atime = time(0L); 385 dir.mtime = dir.atime; 386 dir.length = 0; 387 dir.name = "#¿"; 388 dir.uid = fid->uname; 389 dir.gid = fid->uname; 390 dir.muid = fid->uname; 391 392 if((m->r.nstat = convD2M(&dir, m->data, m->con->msize)) == 0){ 393 vtSetError("stat QTAUTH botch"); 394 fidPut(fid); 395 return 0; 396 } 397 m->r.stat = m->data; 398 399 fidPut(fid); 400 return 1; 401 } 402 if(!fileGetDir(fid->file, &de)){ 403 fidPut(fid); 404 return 0; 405 } 406 fidPut(fid); 407 408 /* 409 * TODO: optimise this copy (in convS2M) away somehow. 410 * This pettifoggery with m->data will do for the moment. 411 */ 412 m->r.nstat = dirDe2M(&de, m->data, m->con->msize); 413 m->r.stat = m->data; 414 deCleanup(&de); 415 416 return 1; 417 } 418 419 static int 420 _rTclunk(Fid* fid, int remove) 421 { 422 int rok; 423 424 if(fid->excl) 425 exclFree(fid); 426 427 rok = 1; 428 if(remove && !(fid->qid.type & QTAUTH)){ 429 if((rok = permParent(fid, PermW)) != 0) 430 rok = fileRemove(fid->file, fid->uid); 431 } 432 fidClunk(fid); 433 434 return rok; 435 } 436 437 static int 438 rTremove(Msg* m) 439 { 440 Fid *fid; 441 442 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil) 443 return 0; 444 return _rTclunk(fid, 1); 445 } 446 447 static int 448 rTclunk(Msg* m) 449 { 450 Fid *fid; 451 452 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil) 453 return 0; 454 _rTclunk(fid, (fid->open & FidORclose)); 455 456 return 1; 457 } 458 459 static int 460 rTwrite(Msg* m) 461 { 462 Fid *fid; 463 int count, n; 464 465 if((fid = fidGet(m->con, m->t.fid, 0)) == nil) 466 return 0; 467 if(!(fid->open & FidOWrite)){ 468 vtSetError("fid not open for write"); 469 goto error; 470 } 471 472 count = m->t.count; 473 if(count < 0 || count > m->con->msize-IOHDRSZ){ 474 vtSetError("write count too big"); 475 goto error; 476 } 477 if(m->t.offset < 0){ 478 vtSetError("write offset negative"); 479 goto error; 480 } 481 if(fid->excl != nil && !exclUpdate(fid)) 482 goto error; 483 484 if(fid->qid.type & QTDIR){ 485 vtSetError("is a directory"); 486 goto error; 487 } 488 else if(fid->qid.type & QTAUTH) 489 n = authWrite(fid, m->t.data, count); 490 else 491 n = fileWrite(fid->file, m->t.data, count, m->t.offset, fid->uid); 492 if(n < 0) 493 goto error; 494 495 496 m->r.count = n; 497 498 fidPut(fid); 499 return 1; 500 501 error: 502 fidPut(fid); 503 return 0; 504 } 505 506 static int 507 rTread(Msg* m) 508 { 509 Fid *fid; 510 uchar *data; 511 int count, n; 512 513 if((fid = fidGet(m->con, m->t.fid, 0)) == nil) 514 return 0; 515 if(!(fid->open & FidORead)){ 516 vtSetError("fid not open for read"); 517 goto error; 518 } 519 520 count = m->t.count; 521 if(count < 0 || count > m->con->msize-IOHDRSZ){ 522 vtSetError("read count too big"); 523 goto error; 524 } 525 if(m->t.offset < 0){ 526 vtSetError("read offset negative"); 527 goto error; 528 } 529 if(fid->excl != nil && !exclUpdate(fid)) 530 goto error; 531 532 /* 533 * TODO: optimise this copy (in convS2M) away somehow. 534 * This pettifoggery with m->data will do for the moment. 535 */ 536 data = m->data+IOHDRSZ; 537 if(fid->qid.type & QTDIR) 538 n = dirRead(fid, data, count, m->t.offset); 539 else if(fid->qid.type & QTAUTH) 540 n = authRead(fid, data, count); 541 else 542 n = fileRead(fid->file, data, count, m->t.offset); 543 if(n < 0) 544 goto error; 545 546 m->r.count = n; 547 m->r.data = (char*)data; 548 549 fidPut(fid); 550 return 1; 551 552 error: 553 fidPut(fid); 554 return 0; 555 } 556 557 static int 558 rTcreate(Msg* m) 559 { 560 Fid *fid; 561 File *file; 562 ulong mode; 563 int omode, open, perm; 564 565 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil) 566 return 0; 567 if(fid->open){ 568 vtSetError("fid open for I/O"); 569 goto error; 570 } 571 if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){ 572 vtSetError("read-only filesystem"); 573 goto error; 574 } 575 if(!fileIsDir(fid->file)){ 576 vtSetError("not a directory"); 577 goto error; 578 } 579 if(!permFid(fid, PermW)) 580 goto error; 581 if(!validFileName(m->t.name)) 582 goto error; 583 if(strcmp(fid->uid, uidnoworld) == 0){ 584 vtSetError(EPermission); 585 goto error; 586 } 587 588 omode = m->t.mode & OMODE; 589 open = 0; 590 591 if(omode == OREAD || omode == ORDWR || omode == OEXEC) 592 open |= FidORead; 593 if(omode == OWRITE || omode == ORDWR) 594 open |= FidOWrite; 595 if((open & (FidOWrite|FidORead)) == 0){ 596 vtSetError("unknown mode"); 597 goto error; 598 } 599 if(m->t.perm & DMDIR){ 600 if((m->t.mode & (ORCLOSE|OTRUNC)) || (open & FidOWrite)){ 601 vtSetError("illegal mode"); 602 goto error; 603 } 604 if(m->t.perm & DMAPPEND){ 605 vtSetError("illegal perm"); 606 goto error; 607 } 608 } 609 610 mode = fileGetMode(fid->file); 611 perm = m->t.perm; 612 if(m->t.perm & DMDIR) 613 perm &= ~0777|(mode & 0777); 614 else 615 perm &= ~0666|(mode & 0666); 616 mode = perm & 0777; 617 if(m->t.perm & DMDIR) 618 mode |= ModeDir; 619 if(m->t.perm & DMAPPEND) 620 mode |= ModeAppend; 621 if(m->t.perm & DMEXCL) 622 mode |= ModeExclusive; 623 624 if((file = fileCreate(fid->file, m->t.name, mode, fid->uid)) == nil){ 625 fidPut(fid); 626 return 0; 627 } 628 fileDecRef(fid->file); 629 630 fid->qid.vers = fileGetMcount(file); 631 fid->qid.path = fileGetId(file); 632 fid->file = file; 633 mode = fileGetMode(fid->file); 634 if(mode & ModeDir) 635 fid->qid.type = QTDIR; 636 else 637 fid->qid.type = QTFILE; 638 if(mode & ModeAppend) 639 fid->qid.type |= QTAPPEND; 640 if(mode & ModeExclusive){ 641 fid->qid.type |= QTEXCL; 642 assert(exclAlloc(fid) != 0); 643 } 644 if(m->t.mode & ORCLOSE) 645 open |= FidORclose; 646 fid->open = open; 647 648 m->r.qid = fid->qid; 649 m->r.iounit = m->con->msize-IOHDRSZ; 650 651 fidPut(fid); 652 return 1; 653 654 error: 655 fidPut(fid); 656 return 0; 657 } 658 659 static int 660 rTopen(Msg* m) 661 { 662 Fid *fid; 663 int isdir, mode, omode, open, rofs; 664 665 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil) 666 return 0; 667 if(fid->open){ 668 vtSetError("fid open for I/O"); 669 goto error; 670 } 671 672 isdir = fileIsDir(fid->file); 673 open = 0; 674 rofs = fileIsRoFs(fid->file) || !groupWriteMember(fid->uname); 675 676 if(m->t.mode & ORCLOSE){ 677 if(isdir){ 678 vtSetError("is a directory"); 679 goto error; 680 } 681 if(rofs){ 682 vtSetError("read-only filesystem"); 683 goto error; 684 } 685 if(!permParent(fid, PermW)) 686 goto error; 687 688 open |= FidORclose; 689 } 690 691 omode = m->t.mode & OMODE; 692 if(omode == OREAD || omode == ORDWR){ 693 if(!permFid(fid, PermR)) 694 goto error; 695 open |= FidORead; 696 } 697 if(omode == OWRITE || omode == ORDWR || (m->t.mode & OTRUNC)){ 698 if(isdir){ 699 vtSetError("is a directory"); 700 goto error; 701 } 702 if(rofs){ 703 vtSetError("read-only filesystem"); 704 goto error; 705 } 706 if(!permFid(fid, PermW)) 707 goto error; 708 open |= FidOWrite; 709 } 710 if(omode == OEXEC){ 711 if(isdir){ 712 vtSetError("is a directory"); 713 goto error; 714 } 715 if(!permFid(fid, PermX)) 716 goto error; 717 open |= FidORead; 718 } 719 if((open & (FidOWrite|FidORead)) == 0){ 720 vtSetError("unknown mode"); 721 goto error; 722 } 723 724 mode = fileGetMode(fid->file); 725 if((mode & ModeExclusive) && exclAlloc(fid) == 0) 726 goto error; 727 728 /* 729 * Everything checks out, try to commit any changes. 730 */ 731 if((m->t.mode & OTRUNC) && !(mode & ModeAppend)){ 732 if(!fileTruncate(fid->file, fid->uid)) 733 goto error; 734 fid->qid.vers = fileGetMcount(fid->file); 735 } 736 if(isdir && fid->db != nil){ 737 dirBufFree(fid->db); 738 fid->db = nil; 739 } 740 741 m->r.qid = fid->qid; 742 m->r.iounit = m->con->msize-IOHDRSZ; 743 744 fid->open = open; 745 746 fidPut(fid); 747 return 1; 748 749 error: 750 if(fid->excl != nil) 751 exclFree(fid); 752 fidPut(fid); 753 return 0; 754 } 755 756 static int 757 rTwalk(Msg* m) 758 { 759 Qid qid; 760 Fcall *r, *t; 761 int nwname, wlock; 762 File *file, *nfile; 763 Fid *fid, *ofid, *nfid; 764 765 t = &m->t; 766 if(t->fid == t->newfid) 767 wlock = FidFWlock; 768 else 769 wlock = 0; 770 771 /* 772 * The file identified by t->fid must be valid in the 773 * current session and must not have been opened for I/O 774 * by an open or create message. 775 */ 776 if((ofid = fidGet(m->con, t->fid, wlock)) == nil) 777 return 0; 778 if(ofid->open){ 779 vtSetError("file open for I/O"); 780 fidPut(ofid); 781 return 0; 782 } 783 784 /* 785 * If newfid is not the same as fid, allocate a new file; 786 * a side effect is checking newfid is not already in use (error); 787 * if there are no names to walk this will be equivalent to a 788 * simple 'clone' operation. 789 * It's a no-op if newfid is the same as fid and t->nwname is 0. 790 */ 791 nfid = nil; 792 if(t->fid != t->newfid){ 793 nfid = fidGet(m->con, t->newfid, FidFWlock|FidFCreate); 794 if(nfid == nil){ 795 vtSetError("fid in use"); 796 fidPut(ofid); 797 return 0; 798 } 799 nfid->open = ofid->open & ~FidORclose; 800 nfid->file = fileIncRef(ofid->file); 801 nfid->qid = ofid->qid; 802 nfid->uid = vtStrDup(ofid->uid); 803 nfid->uname = vtStrDup(ofid->uname); 804 nfid->fsys = fsysIncRef(ofid->fsys); 805 fid = nfid; 806 } 807 else 808 fid = ofid; 809 810 r = &m->r; 811 r->nwqid = 0; 812 813 if(t->nwname == 0){ 814 if(nfid != nil) 815 fidPut(nfid); 816 fidPut(ofid); 817 818 return 1; 819 } 820 821 file = fid->file; 822 fileIncRef(file); 823 qid = fid->qid; 824 825 for(nwname = 0; nwname < t->nwname; nwname++){ 826 /* 827 * Walked elements must represent a directory and 828 * the implied user must have permission to search 829 * the directory. Walking .. is always allowed, so that 830 * you can't walk into a directory and then not be able 831 * to walk out of it. 832 */ 833 if(!(qid.type & QTDIR)){ 834 vtSetError("not a directory"); 835 break; 836 } 837 if(!permFile(file, fid, PermX) && strcmp(t->wname[nwname], "..") != 0) 838 break; 839 if((nfile = fileWalk(file, t->wname[nwname])) == nil) 840 break; 841 fileDecRef(file); 842 file = nfile; 843 qid.type = QTFILE; 844 if(fileIsDir(file)) 845 qid.type = QTDIR; 846 qid.vers = fileGetMcount(file); 847 qid.path = fileGetId(file); 848 r->wqid[r->nwqid++] = qid; 849 } 850 851 if(nwname == t->nwname){ 852 /* 853 * Walked all elements. Update the target fid 854 * from the temporary qid used during the walk, 855 * and tidy up. 856 */ 857 fid->qid = r->wqid[r->nwqid-1]; 858 fileDecRef(fid->file); 859 fid->file = file; 860 861 if(nfid != nil) 862 fidPut(nfid); 863 864 fidPut(ofid); 865 return 1; 866 } 867 868 /* 869 * Didn't walk all elements, 'clunk' nfid if it exists 870 * and leave fid untouched. 871 * It's not an error if some of the elements were walked OK. 872 */ 873 fileDecRef(file); 874 if(nfid != nil) 875 fidClunk(nfid); 876 877 fidPut(ofid); 878 if(nwname == 0) 879 return 0; 880 return 1; 881 } 882 883 static int 884 rTflush(Msg* m) 885 { 886 if(m->t.oldtag != NOTAG) 887 msgFlush(m); 888 return 1; 889 } 890 891 static void 892 parseAname(char *aname, char **fsname, char **path) 893 { 894 char *s; 895 896 if(aname && aname[0]) 897 s = vtStrDup(aname); 898 else 899 s = vtStrDup("main/active"); 900 *fsname = s; 901 if((*path = strchr(s, '/')) != nil) 902 *(*path)++ = '\0'; 903 else 904 *path = ""; 905 } 906 907 static int 908 rTattach(Msg* m) 909 { 910 Fid *fid; 911 Fsys *fsys; 912 char *fsname, *path; 913 914 if((fid = fidGet(m->con, m->t.fid, FidFWlock|FidFCreate)) == nil) 915 return 0; 916 917 parseAname(m->t.aname, &fsname, &path); 918 if((fsys = fsysGet(fsname)) == nil){ 919 fidClunk(fid); 920 vtMemFree(fsname); 921 return 0; 922 } 923 fid->fsys = fsys; 924 925 if(m->t.uname[0] != '\0') 926 fid->uname = vtStrDup(m->t.uname); 927 else 928 fid->uname = vtStrDup(unamenone); 929 930 if(fsysNoAuthCheck(fsys)){ 931 if((fid->uid = uidByUname(fid->uname)) == nil) 932 fid->uid = vtStrDup(unamenone); 933 } 934 else if(!authCheck(&m->t, fid, fsys)){ 935 fidClunk(fid); 936 vtMemFree(fsname); 937 vtSetError("authentication failed"); 938 return 0; 939 } 940 941 fsysFsRlock(fsys); 942 if((fid->file = fsysGetRoot(fsys, path)) == nil){ 943 fsysFsRUnlock(fsys); 944 fidClunk(fid); 945 vtMemFree(fsname); 946 return 0; 947 } 948 fsysFsRUnlock(fsys); 949 vtMemFree(fsname); 950 951 fid->qid = (Qid){fileGetId(fid->file), 0, QTDIR}; 952 m->r.qid = fid->qid; 953 954 fidPut(fid); 955 return 1; 956 } 957 958 static int 959 rTauth(Msg* m) 960 { 961 int afd; 962 Con *con; 963 Fid *afid; 964 Fsys *fsys; 965 char *fsname, *path; 966 967 parseAname(m->t.aname, &fsname, &path); 968 if((fsys = fsysGet(fsname)) == nil){ 969 vtMemFree(fsname); 970 return 0; 971 } 972 vtMemFree(fsname); 973 974 if(fsysNoAuthCheck(fsys)){ 975 m->con->aok = 1; 976 vtSetError("authentication disabled"); 977 fsysPut(fsys); 978 return 0; 979 } 980 if(strcmp(m->t.uname, unamenone) == 0){ 981 vtSetError("user 'none' requires no authentication"); 982 fsysPut(fsys); 983 return 0; 984 } 985 986 con = m->con; 987 if((afid = fidGet(con, m->t.afid, FidFWlock|FidFCreate)) == nil){ 988 fsysPut(fsys); 989 return 0; 990 } 991 afid->fsys = fsys; 992 993 if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0){ 994 vtSetError("can't open \"/mnt/factotum/rpc\""); 995 fidClunk(afid); 996 return 0; 997 } 998 if((afid->rpc = auth_allocrpc(afd)) == nil){ 999 close(afd); 1000 vtSetError("can't auth_allocrpc"); 1001 fidClunk(afid); 1002 return 0; 1003 } 1004 if(auth_rpc(afid->rpc, "start", "proto=p9any role=server", 23) != ARok){ 1005 vtSetError("can't auth_rpc"); 1006 fidClunk(afid); 1007 return 0; 1008 } 1009 1010 afid->open = FidOWrite|FidORead; 1011 afid->qid.type = QTAUTH; 1012 afid->qid.path = m->t.afid; 1013 afid->uname = vtStrDup(m->t.uname); 1014 1015 m->r.qid = afid->qid; 1016 1017 fidPut(afid); 1018 return 1; 1019 } 1020 1021 static int 1022 rTversion(Msg* m) 1023 { 1024 int v; 1025 Con *con; 1026 Fcall *r, *t; 1027 1028 t = &m->t; 1029 r = &m->r; 1030 con = m->con; 1031 1032 vtLock(con->lock); 1033 if(con->state != ConInit){ 1034 vtUnlock(con->lock); 1035 vtSetError("Tversion: down"); 1036 return 0; 1037 } 1038 con->state = ConNew; 1039 1040 /* 1041 * Release the karma of past lives and suffering. 1042 * Should this be done before or after checking the 1043 * validity of the Tversion? 1044 */ 1045 fidClunkAll(con); 1046 1047 if(t->tag != NOTAG){ 1048 vtUnlock(con->lock); 1049 vtSetError("Tversion: invalid tag"); 1050 return 0; 1051 } 1052 1053 if(t->msize < 256){ 1054 vtUnlock(con->lock); 1055 vtSetError("Tversion: message size too small"); 1056 return 0; 1057 } 1058 if(t->msize < con->msize) 1059 r->msize = t->msize; 1060 else 1061 r->msize = con->msize; 1062 1063 r->version = "unknown"; 1064 if(t->version[0] == '9' && t->version[1] == 'P'){ 1065 /* 1066 * Currently, the only defined version 1067 * is "9P2000"; ignore any later versions. 1068 */ 1069 v = strtol(&t->version[2], 0, 10); 1070 if(v >= 2000){ 1071 r->version = VERSION9P; 1072 con->msize = r->msize; 1073 con->state = ConUp; 1074 } 1075 else if(strcmp(t->version, "9PEoF") == 0){ 1076 r->version = "9PEoF"; 1077 con->msize = r->msize; 1078 con->state = ConMoribund; 1079 } 1080 } 1081 vtUnlock(con->lock); 1082 1083 return 1; 1084 } 1085 1086 int (*rFcall[Tmax])(Msg*) = { 1087 [Tversion] = rTversion, 1088 [Tauth] = rTauth, 1089 [Tattach] = rTattach, 1090 [Tflush] = rTflush, 1091 [Twalk] = rTwalk, 1092 [Topen] = rTopen, 1093 [Tcreate] = rTcreate, 1094 [Tread] = rTread, 1095 [Twrite] = rTwrite, 1096 [Tclunk] = rTclunk, 1097 [Tremove] = rTremove, 1098 [Tstat] = rTstat, 1099 [Twstat] = rTwstat, 1100 }; 1101