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