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