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