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