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