1 #include "stdinc.h" 2 #include "dat.h" 3 #include "fns.h" 4 5 #include "9.h" 6 7 typedef struct Fsys Fsys; 8 9 struct Fsys { 10 VtLock* lock; 11 12 char* name; 13 char* dev; 14 char* venti; 15 16 Fs* fs; 17 VtSession* session; 18 int ref; 19 20 int noauth; 21 int noperm; 22 int wstatallow; 23 24 Fsys* next; 25 }; 26 27 static struct { 28 VtLock* lock; 29 Fsys* head; 30 Fsys* tail; 31 32 char* curfsys; 33 } sbox; 34 35 static char *_argv0; 36 #define argv0 _argv0 37 38 static char FsysAll[] = "all"; 39 40 static char EFsysBusy[] = "fsys: '%s' busy"; 41 static char EFsysExists[] = "fsys: '%s' already exists"; 42 static char EFsysNoCurrent[] = "fsys: no current fsys"; 43 static char EFsysNotFound[] = "fsys: '%s' not found"; 44 static char EFsysNotOpen[] = "fsys: '%s' not open"; 45 46 static Fsys* 47 _fsysGet(char* name) 48 { 49 Fsys *fsys; 50 51 if(name == nil || name[0] == '\0') 52 name = "main"; 53 54 vtRLock(sbox.lock); 55 for(fsys = sbox.head; fsys != nil; fsys = fsys->next){ 56 if(strcmp(name, fsys->name) == 0){ 57 fsys->ref++; 58 break; 59 } 60 } 61 vtRUnlock(sbox.lock); 62 if(fsys == nil) 63 vtSetError(EFsysNotFound, name); 64 return fsys; 65 } 66 67 static int 68 cmdPrintConfig(int argc, char* argv[]) 69 { 70 Fsys *fsys; 71 char *usage = "usage: printconfig"; 72 73 ARGBEGIN{ 74 default: 75 return cliError(usage); 76 }ARGEND 77 78 if(argc) 79 return cliError(usage); 80 81 vtRLock(sbox.lock); 82 for(fsys = sbox.head; fsys != nil; fsys = fsys->next){ 83 consPrint("\tfsys %s config %s\n", fsys->name, fsys->dev); 84 if(fsys->venti && fsys->venti[0]) 85 consPrint("\tfsys %s venti %q\n", fsys->name, fsys->venti); 86 } 87 vtRUnlock(sbox.lock); 88 return 1; 89 } 90 91 Fsys* 92 fsysGet(char* name) 93 { 94 Fsys *fsys; 95 96 if((fsys = _fsysGet(name)) == nil) 97 return nil; 98 99 vtLock(fsys->lock); 100 if(fsys->fs == nil){ 101 vtSetError(EFsysNotOpen, fsys->name); 102 vtUnlock(fsys->lock); 103 fsysPut(fsys); 104 return nil; 105 } 106 vtUnlock(fsys->lock); 107 108 return fsys; 109 } 110 111 char* 112 fsysGetName(Fsys* fsys) 113 { 114 return fsys->name; 115 } 116 117 Fsys* 118 fsysIncRef(Fsys* fsys) 119 { 120 vtLock(sbox.lock); 121 fsys->ref++; 122 vtUnlock(sbox.lock); 123 124 return fsys; 125 } 126 127 void 128 fsysPut(Fsys* fsys) 129 { 130 vtLock(sbox.lock); 131 assert(fsys->ref > 0); 132 fsys->ref--; 133 vtUnlock(sbox.lock); 134 } 135 136 Fs* 137 fsysGetFs(Fsys* fsys) 138 { 139 assert(fsys != nil && fsys->fs != nil); 140 141 return fsys->fs; 142 } 143 144 void 145 fsysFsRlock(Fsys* fsys) 146 { 147 vtRLock(fsys->fs->elk); 148 } 149 150 void 151 fsysFsRUnlock(Fsys* fsys) 152 { 153 vtRUnlock(fsys->fs->elk); 154 } 155 156 int 157 fsysNoAuthCheck(Fsys* fsys) 158 { 159 return fsys->noauth; 160 } 161 162 int 163 fsysNoPermCheck(Fsys* fsys) 164 { 165 return fsys->noperm; 166 } 167 168 int 169 fsysWstatAllow(Fsys* fsys) 170 { 171 return fsys->wstatallow; 172 } 173 174 static char modechars[] = "YUGalLdHSATs"; 175 static ulong modebits[] = { 176 ModeSticky, 177 ModeSetUid, 178 ModeSetGid, 179 ModeAppend, 180 ModeExclusive, 181 ModeLink, 182 ModeDir, 183 ModeHidden, 184 ModeSystem, 185 ModeArchive, 186 ModeTemporary, 187 ModeSnapshot, 188 0 189 }; 190 191 char* 192 fsysModeString(ulong mode, char *buf) 193 { 194 int i; 195 char *p; 196 197 p = buf; 198 for(i=0; modebits[i]; i++) 199 if(mode & modebits[i]) 200 *p++ = modechars[i]; 201 sprint(p, "%luo", mode&0777); 202 return buf; 203 } 204 205 int 206 fsysParseMode(char* s, ulong* mode) 207 { 208 ulong x, y; 209 char *p; 210 211 x = 0; 212 for(; *s < '0' || *s > '9'; s++){ 213 if(*s == 0) 214 return 0; 215 p = strchr(modechars, *s); 216 if(p == nil) 217 return 0; 218 x |= modebits[p-modechars]; 219 } 220 y = strtoul(s, &p, 8); 221 if(*p != '\0' || y > 0777) 222 return 0; 223 *mode = x|y; 224 return 1; 225 } 226 227 File* 228 fsysGetRoot(Fsys* fsys, char* name) 229 { 230 File *root, *sub; 231 232 assert(fsys != nil && fsys->fs != nil); 233 234 root = fsGetRoot(fsys->fs); 235 if(name == nil || strcmp(name, "") == 0) 236 return root; 237 238 sub = fileWalk(root, name); 239 fileDecRef(root); 240 241 return sub; 242 } 243 244 static Fsys* 245 fsysAlloc(char* name, char* dev) 246 { 247 Fsys *fsys; 248 249 vtLock(sbox.lock); 250 for(fsys = sbox.head; fsys != nil; fsys = fsys->next){ 251 if(strcmp(fsys->name, name) != 0) 252 continue; 253 vtSetError(EFsysExists, name); 254 vtUnlock(sbox.lock); 255 return nil; 256 } 257 258 fsys = vtMemAllocZ(sizeof(Fsys)); 259 fsys->lock = vtLockAlloc(); 260 fsys->name = vtStrDup(name); 261 fsys->dev = vtStrDup(dev); 262 263 fsys->ref = 1; 264 265 if(sbox.tail != nil) 266 sbox.tail->next = fsys; 267 else 268 sbox.head = fsys; 269 sbox.tail = fsys; 270 vtUnlock(sbox.lock); 271 272 return fsys; 273 } 274 275 static int 276 fsysClose(Fsys* fsys, int argc, char* argv[]) 277 { 278 char *usage = "usage: [fsys name] close"; 279 280 ARGBEGIN{ 281 default: 282 return cliError(usage); 283 }ARGEND 284 if(argc) 285 return cliError(usage); 286 287 return cliError("close isn't working yet; sync and then kill fossil"); 288 289 /* 290 * Oooh. This could be hard. What if fsys->ref != 1? 291 * Also, fsClose() either does the job or panics, can we 292 * gracefully detect it's still busy? 293 * 294 * More thought and care needed here. 295 */ 296 fsClose(fsys->fs); 297 fsys->fs = nil; 298 vtClose(fsys->session); 299 fsys->session = nil; 300 301 if(sbox.curfsys != nil && strcmp(fsys->name, sbox.curfsys) == 0){ 302 sbox.curfsys = nil; 303 consPrompt(nil); 304 } 305 306 return 1; 307 } 308 309 static int 310 fsysVac(Fsys* fsys, int argc, char* argv[]) 311 { 312 uchar score[VtScoreSize]; 313 char *usage = "usage: [fsys name] vac path"; 314 315 ARGBEGIN{ 316 default: 317 return cliError(usage); 318 }ARGEND 319 if(argc != 1) 320 return cliError(usage); 321 322 if(!fsVac(fsys->fs, argv[0], score)) 323 return 0; 324 325 consPrint("vac:%V\n", score); 326 return 1; 327 } 328 329 static int 330 fsysSnap(Fsys* fsys, int argc, char* argv[]) 331 { 332 int doarchive; 333 char *usage = "usage: [fsys name] snap [-a]"; 334 335 doarchive = 0; 336 ARGBEGIN{ 337 default: 338 return cliError(usage); 339 case 'a': 340 doarchive = 1; 341 break; 342 }ARGEND 343 if(argc) 344 return cliError(usage); 345 346 if(!fsSnapshot(fsys->fs, doarchive)) 347 return 0; 348 349 return 1; 350 } 351 352 static int 353 fsysSnapClean(Fsys *fsys, int argc, char* argv[]) 354 { 355 u32int arch, snap, life; 356 char *usage = "usage: [fsys name] snapclean [maxminutes]\n"; 357 358 ARGBEGIN{ 359 default: 360 return cliError(usage); 361 }ARGEND 362 363 if(argc > 1) 364 return cliError(usage); 365 if(argc == 1) 366 life = atoi(argv[1]); 367 else 368 snapGetTimes(fsys->fs->snap, &arch, &snap, &life); 369 370 fsSnapshotCleanup(fsys->fs, life); 371 return 1; 372 } 373 374 static int 375 fsysSnapTime(Fsys* fsys, int argc, char* argv[]) 376 { 377 char buf[128], *x; 378 int hh, mm, changed; 379 u32int arch, snap, life; 380 char *usage = "usage: [fsys name] snaptime [-a hhmm] [-s snapminutes] [-t maxminutes]"; 381 382 changed = 0; 383 snapGetTimes(fsys->fs->snap, &arch, &snap, &life); 384 ARGBEGIN{ 385 case 'a': 386 changed = 1; 387 x = ARGF(); 388 if(x == nil) 389 return cliError(usage); 390 if(strcmp(x, "none") == 0){ 391 arch = ~(u32int)0; 392 break; 393 } 394 if(strlen(x) != 4 || strspn(x, "0123456789") != 4) 395 return cliError(usage); 396 hh = (x[0]-'0')*10 + x[1]-'0'; 397 mm = (x[2]-'0')*10 + x[3]-'0'; 398 if(hh >= 24 || mm >= 60) 399 return cliError(usage); 400 arch = hh*60+mm; 401 break; 402 case 's': 403 changed = 1; 404 x = ARGF(); 405 if(x == nil) 406 return cliError(usage); 407 if(strcmp(x, "none") == 0){ 408 snap = ~(u32int)0; 409 break; 410 } 411 snap = atoi(x); 412 break; 413 case 't': 414 changed = 1; 415 x = ARGF(); 416 if(x == nil) 417 return cliError(usage); 418 if(strcmp(x, "none") == 0){ 419 life = ~(u32int)0; 420 break; 421 } 422 life = atoi(x); 423 break; 424 default: 425 return cliError(usage); 426 }ARGEND 427 if(argc > 0) 428 return cliError(usage); 429 430 if(changed){ 431 snapSetTimes(fsys->fs->snap, arch, snap, life); 432 return 1; 433 } 434 snapGetTimes(fsys->fs->snap, &arch, &snap, &life); 435 if(arch != ~(u32int)0) 436 sprint(buf, "-a %02d%02d", arch/60, arch%60); 437 else 438 sprint(buf, "-a none"); 439 if(snap != ~(u32int)0) 440 sprint(buf+strlen(buf), " -s %d", snap); 441 else 442 sprint(buf+strlen(buf), " -s none"); 443 if(life != ~(u32int)0) 444 sprint(buf+strlen(buf), " -t %ud", life); 445 else 446 sprint(buf+strlen(buf), " -t none"); 447 consPrint("\tsnaptime %s\n", buf); 448 return 1; 449 } 450 451 static int 452 fsysSync(Fsys* fsys, int argc, char* argv[]) 453 { 454 char *usage = "usage: [fsys name] sync"; 455 456 ARGBEGIN{ 457 default: 458 return cliError(usage); 459 }ARGEND 460 if(argc > 0) 461 return cliError(usage); 462 463 fsSync(fsys->fs); 464 return 1; 465 } 466 467 static int 468 fsysHalt(Fsys *fsys, int argc, char* argv[]) 469 { 470 char *usage = "usage: [fsys name] halt"; 471 472 ARGBEGIN{ 473 default: 474 return cliError(usage); 475 }ARGEND 476 if(argc > 0) 477 return cliError(usage); 478 479 fsHalt(fsys->fs); 480 return 1; 481 } 482 483 static int 484 fsysUnhalt(Fsys *fsys, int argc, char* argv[]) 485 { 486 char *usage = "usage: [fsys name] unhalt"; 487 488 ARGBEGIN{ 489 default: 490 return cliError(usage); 491 }ARGEND 492 if(argc > 0) 493 return cliError(usage); 494 495 if(!fsys->fs->halted) 496 return cliError("file system %s not halted", fsys->name); 497 498 fsUnhalt(fsys->fs); 499 return 1; 500 } 501 502 static int 503 fsysRemove(Fsys* fsys, int argc, char* argv[]) 504 { 505 File *file; 506 char *usage = "usage: [fsys name] remove path ..."; 507 508 ARGBEGIN{ 509 default: 510 return cliError(usage); 511 }ARGEND 512 if(argc == 0) 513 return cliError(usage); 514 515 vtRLock(fsys->fs->elk); 516 while(argc > 0){ 517 if((file = fileOpen(fsys->fs, argv[0])) == nil) 518 consPrint("%s: %R\n", argv[0]); 519 else{ 520 if(!fileRemove(file, uidadm)) 521 consPrint("%s: %R\n", argv[0]); 522 fileDecRef(file); 523 } 524 argc--; 525 argv++; 526 } 527 vtRUnlock(fsys->fs->elk); 528 529 return 1; 530 } 531 532 static int 533 fsysClri(Fsys* fsys, int argc, char* argv[]) 534 { 535 char *usage = "usage: [fsys name] clri path ..."; 536 537 ARGBEGIN{ 538 default: 539 return cliError(usage); 540 }ARGEND 541 if(argc == 0) 542 return cliError(usage); 543 544 vtRLock(fsys->fs->elk); 545 while(argc > 0){ 546 if(!fileClriPath(fsys->fs, argv[0], uidadm)) 547 consPrint("clri %s: %R\n", argv[0]); 548 argc--; 549 argv++; 550 } 551 vtRUnlock(fsys->fs->elk); 552 553 return 1; 554 } 555 556 /* 557 * Inspect and edit the labels for blocks on disk. 558 */ 559 static int 560 fsysLabel(Fsys* fsys, int argc, char* argv[]) 561 { 562 Fs *fs; 563 Label l; 564 int n, r; 565 u32int addr; 566 Block *b, *bb; 567 char *usage = "usage: [fsys name] label addr [type state epoch epochClose tag]"; 568 569 ARGBEGIN{ 570 default: 571 return cliError(usage); 572 }ARGEND 573 if(argc != 1 && argc != 6) 574 return cliError(usage); 575 576 r = 0; 577 vtRLock(fsys->fs->elk); 578 579 fs = fsys->fs; 580 addr = strtoul(argv[0], 0, 0); 581 b = cacheLocal(fs->cache, PartData, addr, OReadOnly); 582 if(b == nil) 583 goto Out0; 584 585 l = b->l; 586 consPrint("%slabel %#ux %ud %ud %ud %ud %#x\n", 587 argc==6 ? "old: " : "", addr, l.type, l.state, 588 l.epoch, l.epochClose, l.tag); 589 590 if(argc == 6){ 591 if(strcmp(argv[1], "-") != 0) 592 l.type = atoi(argv[1]); 593 if(strcmp(argv[2], "-") != 0) 594 l.state = atoi(argv[2]); 595 if(strcmp(argv[3], "-") != 0) 596 l.epoch = strtoul(argv[3], 0, 0); 597 if(strcmp(argv[4], "-") != 0) 598 l.epochClose = strtoul(argv[4], 0, 0); 599 if(strcmp(argv[5], "-") != 0) 600 l.tag = strtoul(argv[5], 0, 0); 601 602 consPrint("new: label %#ux %ud %ud %ud %ud %#x\n", 603 addr, l.type, l.state, l.epoch, l.epochClose, l.tag); 604 bb = _blockSetLabel(b, &l); 605 if(bb == nil) 606 goto Out1; 607 n = 0; 608 for(;;){ 609 if(blockWrite(bb)){ 610 while(bb->iostate != BioClean){ 611 assert(bb->iostate == BioWriting); 612 vtSleep(bb->ioready); 613 } 614 break; 615 } 616 consPrint("blockWrite: %R\n"); 617 if(n++ >= 5){ 618 consPrint("giving up\n"); 619 break; 620 } 621 sleep(5*1000); 622 } 623 blockPut(bb); 624 } 625 r = 1; 626 Out1: 627 blockPut(b); 628 Out0: 629 vtRUnlock(fs->elk); 630 631 return r; 632 } 633 634 /* 635 * Inspect and edit the blocks on disk. 636 */ 637 static int 638 fsysBlock(Fsys* fsys, int argc, char* argv[]) 639 { 640 Fs *fs; 641 char *s; 642 Block *b; 643 uchar *buf; 644 u32int addr; 645 int c, count, i, offset; 646 char *usage = "usage: [fsys name] block addr offset [count [data]]"; 647 648 ARGBEGIN{ 649 default: 650 return cliError(usage); 651 }ARGEND 652 if(argc < 2 || argc > 4) 653 return cliError(usage); 654 655 fs = fsys->fs; 656 addr = strtoul(argv[0], 0, 0); 657 offset = strtoul(argv[1], 0, 0); 658 if(offset < 0 || offset >= fs->blockSize){ 659 vtSetError("bad offset"); 660 return 0; 661 } 662 if(argc > 2) 663 count = strtoul(argv[2], 0, 0); 664 else 665 count = 100000000; 666 if(offset+count > fs->blockSize) 667 count = fs->blockSize - count; 668 669 vtRLock(fs->elk); 670 671 b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly); 672 if(b == nil){ 673 vtSetError("cacheLocal %#ux: %R", addr); 674 vtRUnlock(fs->elk); 675 return 0; 676 } 677 678 consPrint("\t%sblock %#ux %ud %ud %.*H\n", 679 argc==4 ? "old: " : "", addr, offset, count, count, b->data+offset); 680 681 if(argc == 4){ 682 s = argv[3]; 683 if(strlen(s) != 2*count){ 684 vtSetError("bad data count"); 685 goto Out; 686 } 687 buf = vtMemAllocZ(count); 688 for(i = 0; i < count*2; i++){ 689 if(s[i] >= '0' && s[i] <= '9') 690 c = s[i] - '0'; 691 else if(s[i] >= 'a' && s[i] <= 'f') 692 c = s[i] - 'a' + 10; 693 else if(s[i] >= 'A' && s[i] <= 'F') 694 c = s[i] - 'A' + 10; 695 else{ 696 vtSetError("bad hex"); 697 vtMemFree(buf); 698 goto Out; 699 } 700 if((i & 1) == 0) 701 c <<= 4; 702 buf[i>>1] |= c; 703 } 704 memmove(b->data+offset, buf, count); 705 consPrint("\tnew: block %#ux %ud %ud %.*H\n", 706 addr, offset, count, count, b->data+offset); 707 blockDirty(b); 708 } 709 710 Out: 711 blockPut(b); 712 vtRUnlock(fs->elk); 713 714 return 1; 715 } 716 717 /* 718 * Free a disk block. 719 */ 720 static int 721 fsysBfree(Fsys* fsys, int argc, char* argv[]) 722 { 723 Fs *fs; 724 Label l; 725 char *p; 726 Block *b; 727 u32int addr; 728 char *usage = "usage: [fsys name] bfree addr ..."; 729 730 ARGBEGIN{ 731 default: 732 return cliError(usage); 733 }ARGEND 734 if(argc == 0) 735 return cliError(usage); 736 737 fs = fsys->fs; 738 vtRLock(fs->elk); 739 while(argc > 0){ 740 addr = strtoul(argv[0], &p, 0); 741 if(*p != '\0'){ 742 consPrint("bad address - '%s'\n", addr); 743 /* syntax error; let's stop */ 744 vtRUnlock(fs->elk); 745 return 0; 746 } 747 b = cacheLocal(fs->cache, PartData, addr, OReadOnly); 748 if(b == nil){ 749 consPrint("loading %#ux: %R\n", addr); 750 continue; 751 } 752 l = b->l; 753 consPrint("label %#ux %ud %ud %ud %ud %#x\n", 754 addr, l.type, l.state, l.epoch, l.epochClose, l.tag); 755 l.state = BsFree; 756 l.type = BtMax; 757 l.tag = 0; 758 l.epoch = 0; 759 l.epochClose = 0; 760 if(!blockSetLabel(b, &l)) 761 consPrint("freeing %#ux: %R\n", addr); 762 blockPut(b); 763 argc--; 764 argv++; 765 } 766 vtRUnlock(fs->elk); 767 768 return 1; 769 } 770 771 static int 772 fsysDf(Fsys *fsys, int argc, char* argv[]) 773 { 774 char *usage = "usage: [fsys name] df"; 775 u32int used, tot, bsize; 776 Fs *fs; 777 778 ARGBEGIN{ 779 default: 780 return cliError(usage); 781 }ARGEND 782 if(argc != 0) 783 return cliError(usage); 784 785 fs = fsys->fs; 786 cacheCountUsed(fs->cache, fs->elo, &used, &tot, &bsize); 787 consPrint("\t%s: %,llud used + %,llud free = %,llud (%ud%% used)\n", 788 fsys->name, used*(vlong)bsize, (tot-used)*(vlong)bsize, 789 tot*(vlong)bsize, used*100/tot); 790 return 1; 791 } 792 793 /* 794 * Zero an entry or a pointer. 795 */ 796 static int 797 fsysClrep(Fsys* fsys, int argc, char* argv[], int ch) 798 { 799 Fs *fs; 800 Entry e; 801 Block *b; 802 u32int addr; 803 int i, max, offset, sz; 804 uchar zero[VtEntrySize]; 805 char *usage = "usage: [fsys name] clr%c addr offset ..."; 806 807 ARGBEGIN{ 808 default: 809 return cliError(usage, ch); 810 }ARGEND 811 if(argc < 2) 812 return cliError(usage, ch); 813 814 fs = fsys->fs; 815 vtRLock(fsys->fs->elk); 816 817 addr = strtoul(argv[0], 0, 0); 818 b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly); 819 if(b == nil){ 820 vtSetError("cacheLocal %#ux: %R", addr); 821 Err: 822 vtRUnlock(fsys->fs->elk); 823 return 0; 824 } 825 826 switch(ch){ 827 default: 828 vtSetError("clrep"); 829 goto Err; 830 case 'e': 831 if(b->l.type != BtDir){ 832 vtSetError("wrong block type"); 833 goto Err; 834 } 835 sz = VtEntrySize; 836 memset(&e, 0, sizeof e); 837 entryPack(&e, zero, 0); 838 break; 839 case 'p': 840 if(b->l.type == BtDir || b->l.type == BtData){ 841 vtSetError("wrong block type"); 842 goto Err; 843 } 844 sz = VtScoreSize; 845 memmove(zero, vtZeroScore, VtScoreSize); 846 break; 847 } 848 max = fs->blockSize/sz; 849 850 for(i = 1; i < argc; i++){ 851 offset = atoi(argv[i]); 852 if(offset >= max){ 853 consPrint("\toffset %d too large (>= %d)\n", i, max); 854 continue; 855 } 856 consPrint("\tblock %#ux %d %d %.*H\n", addr, offset*sz, sz, sz, b->data+offset*sz); 857 memmove(b->data+offset*sz, zero, sz); 858 } 859 blockDirty(b); 860 blockPut(b); 861 vtRUnlock(fsys->fs->elk); 862 863 return 1; 864 } 865 866 static int 867 fsysClre(Fsys* fsys, int argc, char* argv[]) 868 { 869 return fsysClrep(fsys, argc, argv, 'e'); 870 } 871 872 static int 873 fsysClrp(Fsys* fsys, int argc, char* argv[]) 874 { 875 return fsysClrep(fsys, argc, argv, 'p'); 876 } 877 878 static int 879 fsysEsearch1(File* f, char* s, u32int elo) 880 { 881 int n, r; 882 DirEntry de; 883 DirEntryEnum *dee; 884 File *ff; 885 Entry e, ee; 886 char *t; 887 888 dee = deeOpen(f); 889 if(dee == nil) 890 return 0; 891 892 n = 0; 893 for(;;){ 894 r = deeRead(dee, &de); 895 if(r < 0){ 896 consPrint("\tdeeRead %s/%s: %R\n", s, de.elem); 897 break; 898 } 899 if(r == 0) 900 break; 901 if(de.mode & ModeSnapshot){ 902 if((ff = fileWalk(f, de.elem)) == nil) 903 consPrint("\tcannot walk %s/%s: %R\n", s, de.elem); 904 else{ 905 if(!fileGetSources(ff, &e, &ee, 0)) 906 consPrint("\tcannot get sources for %s/%s: %R\n", s, de.elem); 907 else if(e.snap != 0 && e.snap < elo){ 908 consPrint("\t%ud\tclri %s/%s\n", e.snap, s, de.elem); 909 n++; 910 } 911 fileDecRef(ff); 912 } 913 } 914 else if(de.mode & ModeDir){ 915 if((ff = fileWalk(f, de.elem)) == nil) 916 consPrint("\tcannot walk %s/%s: %R\n", s, de.elem); 917 else{ 918 t = smprint("%s/%s", s, de.elem); 919 n += fsysEsearch1(ff, t, elo); 920 vtMemFree(t); 921 fileDecRef(ff); 922 } 923 } 924 deCleanup(&de); 925 if(r < 0) 926 break; 927 } 928 deeClose(dee); 929 930 return n; 931 } 932 933 static int 934 fsysEsearch(Fs* fs, char* path, u32int elo) 935 { 936 int n; 937 File *f; 938 DirEntry de; 939 940 f = fileOpen(fs, path); 941 if(f == nil) 942 return 0; 943 if(!fileGetDir(f, &de)){ 944 consPrint("\tfileGetDir %s failed: %R\n", path); 945 fileDecRef(f); 946 return 0; 947 } 948 if((de.mode & ModeDir) == 0){ 949 fileDecRef(f); 950 deCleanup(&de); 951 return 0; 952 } 953 deCleanup(&de); 954 n = fsysEsearch1(f, path, elo); 955 fileDecRef(f); 956 return n; 957 } 958 959 static int 960 fsysEpoch(Fsys* fsys, int argc, char* argv[]) 961 { 962 Fs *fs; 963 int force, n, remove; 964 u32int low, old; 965 char *usage = "usage: [fsys name] epoch [[-ry] low]"; 966 967 force = 0; 968 remove = 0; 969 ARGBEGIN{ 970 case 'y': 971 force = 1; 972 break; 973 case 'r': 974 remove = 1; 975 break; 976 default: 977 return cliError(usage); 978 }ARGEND 979 if(argc > 1) 980 return cliError(usage); 981 if(argc > 0) 982 low = strtoul(argv[0], 0, 0); 983 else 984 low = ~(u32int)0; 985 986 fs = fsys->fs; 987 988 vtRLock(fs->elk); 989 consPrint("\tlow %ud hi %ud\n", fs->elo, fs->ehi); 990 n = fsysEsearch(fsys->fs, "/archive", low); 991 n += fsysEsearch(fsys->fs, "/snapshot", low); 992 consPrint("\t%d snapshot%s found with epoch < %ud\n", n, n==1 ? "" : "s", low); 993 vtRUnlock(fs->elk); 994 995 /* 996 * There's a small race here -- a new snapshot with epoch < low might 997 * get introduced now that we unlocked fs->elk. Low has to 998 * be <= fs->ehi. Of course, in order for this to happen low has 999 * to be equal to the current fs->ehi _and_ a snapshot has to 1000 * run right now. This is a small enough window that I don't care. 1001 */ 1002 if(n != 0 && !force){ 1003 consPrint("\tnot setting low epoch\n"); 1004 return 1; 1005 } 1006 old = fs->elo; 1007 if(!fsEpochLow(fs, low)) 1008 consPrint("\tfsEpochLow: %R\n"); 1009 else{ 1010 consPrint("\told: epoch%s %ud\n", force ? " -y" : "", old); 1011 consPrint("\tnew: epoch%s %ud\n", force ? " -y" : "", fs->elo); 1012 if(fs->elo < low) 1013 consPrint("\twarning: new low epoch < old low epoch\n"); 1014 if(force && remove) 1015 fsSnapshotRemove(fs); 1016 } 1017 1018 return 1; 1019 } 1020 1021 static int 1022 fsysCreate(Fsys* fsys, int argc, char* argv[]) 1023 { 1024 int r; 1025 ulong mode; 1026 char *elem, *p, *path; 1027 char *usage = "usage: [fsys name] create path uid gid perm"; 1028 DirEntry de; 1029 File *file, *parent; 1030 1031 ARGBEGIN{ 1032 default: 1033 return cliError(usage); 1034 }ARGEND 1035 if(argc != 4) 1036 return cliError(usage); 1037 1038 if(!fsysParseMode(argv[3], &mode)) 1039 return cliError(usage); 1040 if(mode&ModeSnapshot) 1041 return cliError("create - cannot create with snapshot bit set"); 1042 1043 if(strcmp(argv[1], uidnoworld) == 0) 1044 return cliError("permission denied"); 1045 1046 vtRLock(fsys->fs->elk); 1047 path = vtStrDup(argv[0]); 1048 if((p = strrchr(path, '/')) != nil){ 1049 *p++ = '\0'; 1050 elem = p; 1051 p = path; 1052 if(*p == '\0') 1053 p = "/"; 1054 } 1055 else{ 1056 p = "/"; 1057 elem = path; 1058 } 1059 1060 r = 0; 1061 if((parent = fileOpen(fsys->fs, p)) == nil) 1062 goto out; 1063 1064 file = fileCreate(parent, elem, mode, argv[1]); 1065 fileDecRef(parent); 1066 if(file == nil){ 1067 vtSetError("create %s/%s: %R", p, elem); 1068 goto out; 1069 } 1070 1071 if(!fileGetDir(file, &de)){ 1072 vtSetError("stat failed after create: %R"); 1073 goto out1; 1074 } 1075 1076 if(strcmp(de.gid, argv[2]) != 0){ 1077 vtMemFree(de.gid); 1078 de.gid = vtStrDup(argv[2]); 1079 if(!fileSetDir(file, &de, argv[1])){ 1080 vtSetError("wstat failed after create: %R"); 1081 goto out2; 1082 } 1083 } 1084 r = 1; 1085 1086 out2: 1087 deCleanup(&de); 1088 out1: 1089 fileDecRef(file); 1090 out: 1091 vtMemFree(path); 1092 vtRUnlock(fsys->fs->elk); 1093 1094 return r; 1095 } 1096 1097 static void 1098 fsysPrintStat(char *prefix, char *file, DirEntry *de) 1099 { 1100 char buf[64]; 1101 1102 if(prefix == nil) 1103 prefix = ""; 1104 consPrint("%sstat %q %q %q %q %s %llud\n", prefix, 1105 file, de->elem, de->uid, de->gid, fsysModeString(de->mode, buf), de->size); 1106 } 1107 1108 static int 1109 fsysStat(Fsys* fsys, int argc, char* argv[]) 1110 { 1111 int i; 1112 File *f; 1113 DirEntry de; 1114 char *usage = "usage: [fsys name] stat files..."; 1115 1116 ARGBEGIN{ 1117 default: 1118 return cliError(usage); 1119 }ARGEND 1120 1121 if(argc == 0) 1122 return cliError(usage); 1123 1124 vtRLock(fsys->fs->elk); 1125 for(i=0; i<argc; i++){ 1126 if((f = fileOpen(fsys->fs, argv[i])) == nil){ 1127 consPrint("%s: %R\n"); 1128 continue; 1129 } 1130 if(!fileGetDir(f, &de)){ 1131 consPrint("%s: %R\n"); 1132 fileDecRef(f); 1133 continue; 1134 } 1135 fsysPrintStat("\t", argv[i], &de); 1136 deCleanup(&de); 1137 fileDecRef(f); 1138 } 1139 vtRUnlock(fsys->fs->elk); 1140 return 1; 1141 } 1142 1143 static int 1144 fsysWstat(Fsys *fsys, int argc, char* argv[]) 1145 { 1146 File *f; 1147 char *p; 1148 DirEntry de; 1149 char *usage = "usage: [fsys name] wstat file elem uid gid mode length\n" 1150 "\tuse - for any field to mean don't change"; 1151 1152 ARGBEGIN{ 1153 default: 1154 return cliError(usage); 1155 }ARGEND 1156 1157 if(argc != 6) 1158 return cliError(usage); 1159 1160 vtRLock(fsys->fs->elk); 1161 if((f = fileOpen(fsys->fs, argv[0])) == nil){ 1162 vtSetError("console wstat - walk - %R"); 1163 vtRUnlock(fsys->fs->elk); 1164 return 0; 1165 } 1166 if(!fileGetDir(f, &de)){ 1167 vtSetError("console wstat - stat - %R"); 1168 fileDecRef(f); 1169 vtRUnlock(fsys->fs->elk); 1170 return 0; 1171 } 1172 fsysPrintStat("\told: w", argv[0], &de); 1173 1174 if(strcmp(argv[1], "-") != 0){ 1175 if(!validFileName(argv[1])){ 1176 vtSetError("console wstat - bad elem"); 1177 goto error; 1178 } 1179 vtMemFree(de.elem); 1180 de.elem = vtStrDup(argv[1]); 1181 } 1182 if(strcmp(argv[2], "-") != 0){ 1183 if(!validUserName(argv[2])){ 1184 vtSetError("console wstat - bad uid"); 1185 goto error; 1186 } 1187 vtMemFree(de.uid); 1188 de.uid = vtStrDup(argv[2]); 1189 } 1190 if(strcmp(argv[3], "-") != 0){ 1191 if(!validUserName(argv[3])){ 1192 vtSetError("console wstat - bad gid"); 1193 goto error; 1194 } 1195 vtMemFree(de.gid); 1196 de.gid = vtStrDup(argv[3]); 1197 } 1198 if(strcmp(argv[4], "-") != 0){ 1199 if(!fsysParseMode(argv[4], &de.mode)){ 1200 vtSetError("console wstat - bad mode"); 1201 goto error; 1202 } 1203 } 1204 if(strcmp(argv[5], "-") != 0){ 1205 de.size = strtoull(argv[5], &p, 0); 1206 if(argv[5][0] == '\0' || *p != '\0' || de.size < 0){ 1207 vtSetError("console wstat - bad length"); 1208 goto error; 1209 } 1210 } 1211 1212 if(!fileSetDir(f, &de, uidadm)){ 1213 vtSetError("console wstat - %R"); 1214 goto error; 1215 } 1216 deCleanup(&de); 1217 1218 if(!fileGetDir(f, &de)){ 1219 vtSetError("console wstat - stat2 - %R"); 1220 goto error; 1221 } 1222 fsysPrintStat("\tnew: w", argv[0], &de); 1223 deCleanup(&de); 1224 fileDecRef(f); 1225 vtRUnlock(fsys->fs->elk); 1226 1227 return 1; 1228 1229 error: 1230 deCleanup(&de); /* okay to do this twice */ 1231 fileDecRef(f); 1232 vtRUnlock(fsys->fs->elk); 1233 return 0; 1234 } 1235 1236 static int 1237 fsysVenti(char* name, int argc, char* argv[]) 1238 { 1239 int r; 1240 char *host; 1241 char *usage = "usage: [fsys name] venti [address]"; 1242 Fsys *fsys; 1243 1244 ARGBEGIN{ 1245 default: 1246 return cliError(usage); 1247 }ARGEND 1248 1249 if(argc == 0) 1250 host = nil; 1251 else if(argc == 1) 1252 host = argv[0]; 1253 else 1254 return cliError(usage); 1255 1256 if((fsys = _fsysGet(name)) == nil) 1257 return 0; 1258 1259 vtLock(fsys->lock); 1260 if(host == nil) 1261 host = fsys->venti; 1262 else{ 1263 vtMemFree(fsys->venti); 1264 if(host[0]) 1265 fsys->venti = vtStrDup(host); 1266 else{ 1267 host = nil; 1268 fsys->venti = nil; 1269 } 1270 } 1271 1272 /* already open: do a redial */ 1273 if(fsys->fs != nil){ 1274 if(fsys->session == nil){ 1275 vtSetError("file system was opened with -V"); 1276 r = 0; 1277 goto out; 1278 } 1279 r = 1; 1280 if(!vtRedial(fsys->session, host) 1281 || !vtConnect(fsys->session, 0)) 1282 r = 0; 1283 goto out; 1284 } 1285 1286 /* not yet open: try to dial */ 1287 if(fsys->session) 1288 vtClose(fsys->session); 1289 r = 1; 1290 if((fsys->session = vtDial(host, 0)) == nil 1291 || !vtConnect(fsys->session, 0)) 1292 r = 0; 1293 out: 1294 vtUnlock(fsys->lock); 1295 fsysPut(fsys); 1296 return r; 1297 } 1298 1299 static int 1300 fsysOpen(char* name, int argc, char* argv[]) 1301 { 1302 char *p, *host; 1303 Fsys *fsys; 1304 long ncache; 1305 int noauth, noventi, noperm, rflag, wstatallow; 1306 char *usage = "usage: fsys name open [-APVWr] [-c ncache]"; 1307 1308 ncache = 1000; 1309 noauth = noperm = wstatallow = noventi = 0; 1310 rflag = OReadWrite; 1311 1312 ARGBEGIN{ 1313 default: 1314 return cliError(usage); 1315 case 'A': 1316 noauth = 1; 1317 break; 1318 case 'P': 1319 noperm = 1; 1320 break; 1321 case 'V': 1322 noventi = 1; 1323 break; 1324 case 'W': 1325 wstatallow = 1; 1326 break; 1327 case 'c': 1328 p = ARGF(); 1329 if(p == nil) 1330 return cliError(usage); 1331 ncache = strtol(argv[0], &p, 0); 1332 if(ncache <= 0 || p == argv[0] || *p != '\0') 1333 return cliError(usage); 1334 break; 1335 case 'r': 1336 rflag = OReadOnly; 1337 break; 1338 }ARGEND 1339 if(argc) 1340 return cliError(usage); 1341 1342 if((fsys = _fsysGet(name)) == nil) 1343 return 0; 1344 1345 vtLock(fsys->lock); 1346 if(fsys->fs != nil){ 1347 vtSetError(EFsysBusy, fsys->name); 1348 vtUnlock(fsys->lock); 1349 fsysPut(fsys); 1350 return 0; 1351 } 1352 1353 if(fsys->session == nil){ 1354 if(fsys->venti && fsys->venti[0]) 1355 host = fsys->venti; 1356 else 1357 host = nil; 1358 fsys->session = vtDial(host, 1); 1359 if(!vtConnect(fsys->session, nil) && !noventi) 1360 fprint(2, "warning: connecting to venti: %R\n"); 1361 } 1362 if((fsys->fs = fsOpen(fsys->dev, fsys->session, ncache, rflag)) == nil){ 1363 vtSetError("fsOpen: %R"); 1364 vtUnlock(fsys->lock); 1365 fsysPut(fsys); 1366 return 0; 1367 } 1368 fsys->noauth = noauth; 1369 fsys->noperm = noperm; 1370 fsys->wstatallow = wstatallow; 1371 vtUnlock(fsys->lock); 1372 fsysPut(fsys); 1373 1374 if(strcmp(name, "main") == 0) 1375 usersFileRead(nil); 1376 1377 return 1; 1378 } 1379 1380 static int 1381 fsysUnconfig(char* name, int argc, char* argv[]) 1382 { 1383 Fsys *fsys, **fp; 1384 char *usage = "usage: fsys name unconfig"; 1385 1386 ARGBEGIN{ 1387 default: 1388 return cliError(usage); 1389 }ARGEND 1390 if(argc) 1391 return cliError(usage); 1392 1393 vtLock(sbox.lock); 1394 fp = &sbox.head; 1395 for(fsys = *fp; fsys != nil; fsys = fsys->next){ 1396 if(strcmp(fsys->name, name) == 0) 1397 break; 1398 fp = &fsys->next; 1399 } 1400 if(fsys == nil){ 1401 vtSetError(EFsysNotFound, name); 1402 vtUnlock(sbox.lock); 1403 return 0; 1404 } 1405 if(fsys->ref != 0 || fsys->fs != nil){ 1406 vtSetError(EFsysBusy, fsys->name); 1407 vtUnlock(sbox.lock); 1408 return 0; 1409 } 1410 *fp = fsys->next; 1411 vtUnlock(sbox.lock); 1412 1413 if(fsys->session != nil){ 1414 vtClose(fsys->session); 1415 vtFree(fsys->session); 1416 } 1417 if(fsys->venti != nil) 1418 vtMemFree(fsys->venti); 1419 if(fsys->dev != nil) 1420 vtMemFree(fsys->dev); 1421 if(fsys->name != nil) 1422 vtMemFree(fsys->name); 1423 vtMemFree(fsys); 1424 1425 return 1; 1426 } 1427 1428 static int 1429 fsysConfig(char* name, int argc, char* argv[]) 1430 { 1431 Fsys *fsys; 1432 char *usage = "usage: fsys name config dev"; 1433 1434 ARGBEGIN{ 1435 default: 1436 return cliError(usage); 1437 }ARGEND 1438 if(argc != 1) 1439 return cliError(usage); 1440 1441 if((fsys = _fsysGet(argv[0])) != nil){ 1442 vtLock(fsys->lock); 1443 if(fsys->fs != nil){ 1444 vtSetError(EFsysBusy, fsys->name); 1445 vtUnlock(fsys->lock); 1446 fsysPut(fsys); 1447 return 0; 1448 } 1449 vtMemFree(fsys->dev); 1450 fsys->dev = vtStrDup(argv[0]); 1451 vtUnlock(fsys->lock); 1452 } 1453 else if((fsys = fsysAlloc(name, argv[0])) == nil) 1454 return 0; 1455 1456 fsysPut(fsys); 1457 1458 return 1; 1459 } 1460 1461 static struct { 1462 char* cmd; 1463 int (*f)(Fsys*, int, char**); 1464 int (*f1)(char*, int, char**); 1465 } fsyscmd[] = { 1466 { "close", fsysClose, }, 1467 { "config", nil, fsysConfig, }, 1468 { "open", nil, fsysOpen, }, 1469 { "unconfig", nil, fsysUnconfig, }, 1470 { "venti", nil, fsysVenti, }, 1471 1472 { "bfree", fsysBfree, }, 1473 { "block", fsysBlock, }, 1474 { "clre", fsysClre, }, 1475 { "clri", fsysClri, }, 1476 { "clrp", fsysClrp, }, 1477 { "create", fsysCreate, }, 1478 { "df", fsysDf, }, 1479 { "epoch", fsysEpoch, }, 1480 { "halt", fsysHalt, }, 1481 { "label", fsysLabel, }, 1482 { "remove", fsysRemove, }, 1483 { "snap", fsysSnap, }, 1484 { "snaptime", fsysSnapTime, }, 1485 { "snapclean", fsysSnapClean, }, 1486 { "stat", fsysStat, }, 1487 { "sync", fsysSync, }, 1488 { "unhalt", fsysUnhalt, }, 1489 { "wstat", fsysWstat, }, 1490 { "vac", fsysVac, }, 1491 1492 { nil, nil, }, 1493 }; 1494 1495 static int 1496 fsysXXX1(Fsys *fsys, int i, int argc, char* argv[]) 1497 { 1498 int r; 1499 1500 vtLock(fsys->lock); 1501 if(fsys->fs == nil){ 1502 vtUnlock(fsys->lock); 1503 vtSetError(EFsysNotOpen, fsys->name); 1504 return 0; 1505 } 1506 1507 if(fsys->fs->halted && fsyscmd[i].f != fsysUnhalt){ 1508 vtSetError("file system %s is halted", fsys->name); 1509 vtUnlock(fsys->lock); 1510 return 0; 1511 } 1512 1513 r = (*fsyscmd[i].f)(fsys, argc, argv); 1514 vtUnlock(fsys->lock); 1515 return r; 1516 } 1517 1518 static int 1519 fsysXXX(char* name, int argc, char* argv[]) 1520 { 1521 int i, r; 1522 Fsys *fsys; 1523 1524 for(i = 0; fsyscmd[i].cmd != nil; i++){ 1525 if(strcmp(fsyscmd[i].cmd, argv[0]) == 0) 1526 break; 1527 } 1528 1529 if(fsyscmd[i].cmd == nil){ 1530 vtSetError("unknown command - '%s'", argv[0]); 1531 return 0; 1532 } 1533 1534 /* some commands want the name... */ 1535 if(fsyscmd[i].f1 != nil){ 1536 if(strcmp(name, FsysAll) == 0){ 1537 vtSetError("cannot use fsys %#q with %#q command", FsysAll, argv[0]); 1538 return 0; 1539 } 1540 return (*fsyscmd[i].f1)(name, argc, argv); 1541 } 1542 1543 /* ... but most commands want the Fsys */ 1544 if(strcmp(name, FsysAll) == 0){ 1545 r = 1; 1546 vtRLock(sbox.lock); 1547 for(fsys = sbox.head; fsys != nil; fsys = fsys->next){ 1548 fsys->ref++; 1549 r = fsysXXX1(fsys, i, argc, argv) && r; 1550 fsys->ref--; 1551 } 1552 vtRUnlock(sbox.lock); 1553 }else{ 1554 if((fsys = _fsysGet(name)) == nil) 1555 return 0; 1556 r = fsysXXX1(fsys, i, argc, argv); 1557 fsysPut(fsys); 1558 } 1559 return r; 1560 } 1561 1562 static int 1563 cmdFsysXXX(int argc, char* argv[]) 1564 { 1565 char *name; 1566 1567 if((name = sbox.curfsys) == nil){ 1568 vtSetError(EFsysNoCurrent, argv[0]); 1569 return 0; 1570 } 1571 1572 return fsysXXX(name, argc, argv); 1573 } 1574 1575 static int 1576 cmdFsys(int argc, char* argv[]) 1577 { 1578 Fsys *fsys; 1579 char *usage = "usage: fsys [name ...]"; 1580 1581 ARGBEGIN{ 1582 default: 1583 return cliError(usage); 1584 }ARGEND 1585 1586 if(argc == 0){ 1587 vtRLock(sbox.lock); 1588 for(fsys = sbox.head; fsys != nil; fsys = fsys->next) 1589 consPrint("\t%s\n", fsys->name); 1590 vtRUnlock(sbox.lock); 1591 return 1; 1592 } 1593 if(argc == 1){ 1594 fsys = nil; 1595 if(strcmp(argv[0], FsysAll) != 0 && (fsys = fsysGet(argv[0])) == nil) 1596 return 0; 1597 sbox.curfsys = vtStrDup(argv[0]); 1598 consPrompt(sbox.curfsys); 1599 if(fsys) 1600 fsysPut(fsys); 1601 return 1; 1602 } 1603 1604 return fsysXXX(argv[0], argc-1, argv+1); 1605 } 1606 1607 int 1608 fsysInit(void) 1609 { 1610 int i; 1611 1612 fmtinstall('H', encodefmt); 1613 fmtinstall('V', scoreFmt); 1614 fmtinstall('R', vtErrFmt); 1615 fmtinstall('L', labelFmt); 1616 1617 sbox.lock = vtLockAlloc(); 1618 1619 cliAddCmd("fsys", cmdFsys); 1620 for(i = 0; fsyscmd[i].cmd != nil; i++){ 1621 if(fsyscmd[i].f != nil) 1622 cliAddCmd(fsyscmd[i].cmd, cmdFsysXXX); 1623 } 1624 /* the venti cmd is special: the fs can be either open or closed */ 1625 cliAddCmd("venti", cmdFsysXXX); 1626 cliAddCmd("printconfig", cmdPrintConfig); 1627 1628 return 1; 1629 } 1630