Blob


1 #include "stdinc.h"
2 #include <bio.h>
3 #include "dat.h"
4 #include "fns.h"
5 #include "9.h"
7 struct Fsys {
8 QLock lock;
10 char* name; /* copy here & Fs to ease error reporting */
11 char* dev;
12 char* venti;
14 Fs* fs;
15 VtConn* session;
16 int ref;
18 int noauth;
19 int noperm;
20 int wstatallow;
22 Fsys* next;
23 };
25 int mempcnt; /* from fossil.c */
27 int fsGetBlockSize(Fs *fs);
29 static struct {
30 RWLock lock;
31 Fsys* head;
32 Fsys* tail;
34 char* curfsys;
35 } sbox;
37 static char *_argv0;
38 #define argv0 _argv0
40 static char FsysAll[] = "all";
42 static char EFsysBusy[] = "fsys: '%s' busy";
43 static char EFsysExists[] = "fsys: '%s' already exists";
44 static char EFsysNoCurrent[] = "fsys: no current fsys";
45 static char EFsysNotFound[] = "fsys: '%s' not found";
46 static char EFsysNotOpen[] = "fsys: '%s' not open";
48 static char *
49 ventihost(char *host)
50 {
51 if(host != nil)
52 return vtstrdup(host);
53 host = getenv("venti");
54 if(host == nil)
55 host = vtstrdup("$venti");
56 return host;
57 }
59 static void
60 prventihost(char *host)
61 {
62 char *vh;
64 vh = ventihost(host);
65 fprint(2, "%s: dialing venti at %s\n",
66 argv0, netmkaddr(vh, 0, "venti"));
67 free(vh);
68 }
70 static VtConn *
71 myDial(char *host)
72 {
73 prventihost(host);
74 return vtdial(host);
75 }
77 static int
78 myRedial(VtConn *z, char *host)
79 {
80 prventihost(host);
81 return vtredial(z, host);
82 }
84 static Fsys*
85 _fsysGet(char* name)
86 {
87 Fsys *fsys;
89 if(name == nil || name[0] == '\0')
90 name = "main";
92 rlock(&sbox.lock);
93 for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
94 if(strcmp(name, fsys->name) == 0){
95 fsys->ref++;
96 break;
97 }
98 }
99 runlock(&sbox.lock);
100 if(fsys == nil)
101 werrstr(EFsysNotFound, name);
102 return fsys;
105 static int
106 cmdPrintConfig(int argc, char* argv[])
108 Fsys *fsys;
109 char *usage = "usage: printconfig";
111 ARGBEGIN{
112 default:
113 return cliError(usage);
114 }ARGEND
116 if(argc)
117 return cliError(usage);
119 rlock(&sbox.lock);
120 for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
121 consPrint("\tfsys %s config %s\n", fsys->name, fsys->dev);
122 if(fsys->venti && fsys->venti[0])
123 consPrint("\tfsys %s venti %q\n", fsys->name,
124 fsys->venti);
126 runlock(&sbox.lock);
127 return 1;
130 Fsys*
131 fsysGet(char* name)
133 Fsys *fsys;
135 if((fsys = _fsysGet(name)) == nil)
136 return nil;
138 qlock(&fsys->lock);
139 if(fsys->fs == nil){
140 werrstr(EFsysNotOpen, fsys->name);
141 qunlock(&fsys->lock);
142 fsysPut(fsys);
143 return nil;
145 qunlock(&fsys->lock);
147 return fsys;
150 char*
151 fsysGetName(Fsys* fsys)
153 return fsys->name;
156 Fsys*
157 fsysIncRef(Fsys* fsys)
159 wlock(&sbox.lock);
160 fsys->ref++;
161 wunlock(&sbox.lock);
163 return fsys;
166 void
167 fsysPut(Fsys* fsys)
169 wlock(&sbox.lock);
170 assert(fsys->ref > 0);
171 fsys->ref--;
172 wunlock(&sbox.lock);
175 Fs*
176 fsysGetFs(Fsys* fsys)
178 assert(fsys != nil && fsys->fs != nil);
180 return fsys->fs;
183 void
184 fsysFsRlock(Fsys* fsys)
186 rlock(&fsys->fs->elk);
189 void
190 fsysFsRUnlock(Fsys* fsys)
192 runlock(&fsys->fs->elk);
195 int
196 fsysNoAuthCheck(Fsys* fsys)
198 return fsys->noauth;
201 int
202 fsysNoPermCheck(Fsys* fsys)
204 return fsys->noperm;
207 int
208 fsysWstatAllow(Fsys* fsys)
210 return fsys->wstatallow;
213 static char modechars[] = "YUGalLdHSATs";
214 static ulong modebits[] = {
215 ModeSticky,
216 ModeSetUid,
217 ModeSetGid,
218 ModeAppend,
219 ModeExclusive,
220 ModeLink,
221 ModeDir,
222 ModeHidden,
223 ModeSystem,
224 ModeArchive,
225 ModeTemporary,
226 ModeSnapshot,
228 };
230 char*
231 fsysModeString(ulong mode, char *buf)
233 int i;
234 char *p;
236 p = buf;
237 for(i=0; modebits[i]; i++)
238 if(mode & modebits[i])
239 *p++ = modechars[i];
240 sprint(p, "%luo", mode&0777);
241 return buf;
244 int
245 fsysParseMode(char* s, ulong* mode)
247 ulong x, y;
248 char *p;
250 x = 0;
251 for(; *s < '0' || *s > '9'; s++){
252 if(*s == 0)
253 return 0;
254 p = strchr(modechars, *s);
255 if(p == nil)
256 return 0;
257 x |= modebits[p-modechars];
259 y = strtoul(s, &p, 8);
260 if(*p != '\0' || y > 0777)
261 return 0;
262 *mode = x|y;
263 return 1;
266 File*
267 fsysGetRoot(Fsys* fsys, char* name)
269 File *root, *sub;
271 assert(fsys != nil && fsys->fs != nil);
273 root = fsGetRoot(fsys->fs);
274 if(name == nil || strcmp(name, "") == 0)
275 return root;
277 sub = fileWalk(root, name);
278 fileDecRef(root);
280 return sub;
283 static Fsys*
284 fsysAlloc(char* name, char* dev)
286 Fsys *fsys;
288 wlock(&sbox.lock);
289 for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
290 if(strcmp(fsys->name, name) != 0)
291 continue;
292 werrstr(EFsysExists, name);
293 wunlock(&sbox.lock);
294 return nil;
297 fsys = vtmallocz(sizeof(Fsys));
298 fsys->name = vtstrdup(name);
299 fsys->dev = vtstrdup(dev);
301 fsys->ref = 1;
303 if(sbox.tail != nil)
304 sbox.tail->next = fsys;
305 else
306 sbox.head = fsys;
307 sbox.tail = fsys;
308 wunlock(&sbox.lock);
310 return fsys;
313 static int
314 fsysClose(Fsys* fsys, int argc, char* argv[])
316 char *usage = "usage: [fsys name] close";
318 ARGBEGIN{
319 default:
320 return cliError(usage);
321 }ARGEND
322 if(argc)
323 return cliError(usage);
325 return cliError("close isn't working yet; halt %s and then kill fossil",
326 fsys->name);
328 /*
329 * Oooh. This could be hard. What if fsys->ref != 1?
330 * Also, fsClose() either does the job or panics, can we
331 * gracefully detect it's still busy?
333 * More thought and care needed here.
334 fsClose(fsys->fs);
335 fsys->fs = nil;
336 vtfreeconn(fsys->session);
337 fsys->session = nil;
339 if(sbox.curfsys != nil && strcmp(fsys->name, sbox.curfsys) == 0){
340 sbox.curfsys = nil;
341 consPrompt(nil);
344 return 1;
345 */
348 static int
349 fsysVac(Fsys* fsys, int argc, char* argv[])
351 uchar score[VtScoreSize];
352 char *usage = "usage: [fsys name] vac path";
354 ARGBEGIN{
355 default:
356 return cliError(usage);
357 }ARGEND
358 if(argc != 1)
359 return cliError(usage);
361 if(!fsVac(fsys->fs, argv[0], score))
362 return 0;
364 consPrint("vac:%V\n", score);
365 return 1;
368 static int
369 fsysSnap(Fsys* fsys, int argc, char* argv[])
371 int doarchive;
372 char *usage = "usage: [fsys name] snap [-a] [-s /active] [-d /archive/yyyy/mmmm]";
373 char *src, *dst;
375 src = nil;
376 dst = nil;
377 doarchive = 0;
378 ARGBEGIN{
379 default:
380 return cliError(usage);
381 case 'a':
382 doarchive = 1;
383 break;
384 case 'd':
385 if((dst = ARGF()) == nil)
386 return cliError(usage);
387 break;
388 case 's':
389 if((src = ARGF()) == nil)
390 return cliError(usage);
391 break;
392 }ARGEND
393 if(argc)
394 return cliError(usage);
396 if(!fsSnapshot(fsys->fs, src, dst, doarchive))
397 return 0;
399 return 1;
402 static int
403 fsysSnapClean(Fsys *fsys, int argc, char* argv[])
405 u32int arch, snap, life;
406 char *usage = "usage: [fsys name] snapclean [maxminutes]\n";
408 ARGBEGIN{
409 default:
410 return cliError(usage);
411 }ARGEND
413 if(argc > 1)
414 return cliError(usage);
415 if(argc == 1)
416 life = atoi(argv[0]);
417 else
418 snapGetTimes(fsys->fs->snap, &arch, &snap, &life);
420 fsSnapshotCleanup(fsys->fs, life);
421 return 1;
424 static int
425 fsysSnapTime(Fsys* fsys, int argc, char* argv[])
427 char buf[128], *x;
428 int hh, mm, changed;
429 u32int arch, snap, life;
430 char *usage = "usage: [fsys name] snaptime [-a hhmm] [-s snapminutes] [-t maxminutes]";
432 changed = 0;
433 snapGetTimes(fsys->fs->snap, &arch, &snap, &life);
434 ARGBEGIN{
435 case 'a':
436 changed = 1;
437 x = ARGF();
438 if(x == nil)
439 return cliError(usage);
440 if(strcmp(x, "none") == 0){
441 arch = ~(u32int)0;
442 break;
444 if(strlen(x) != 4 || strspn(x, "0123456789") != 4)
445 return cliError(usage);
446 hh = (x[0]-'0')*10 + x[1]-'0';
447 mm = (x[2]-'0')*10 + x[3]-'0';
448 if(hh >= 24 || mm >= 60)
449 return cliError(usage);
450 arch = hh*60+mm;
451 break;
452 case 's':
453 changed = 1;
454 x = ARGF();
455 if(x == nil)
456 return cliError(usage);
457 if(strcmp(x, "none") == 0){
458 snap = ~(u32int)0;
459 break;
461 snap = atoi(x);
462 break;
463 case 't':
464 changed = 1;
465 x = ARGF();
466 if(x == nil)
467 return cliError(usage);
468 if(strcmp(x, "none") == 0){
469 life = ~(u32int)0;
470 break;
472 life = atoi(x);
473 break;
474 default:
475 return cliError(usage);
476 }ARGEND
477 if(argc > 0)
478 return cliError(usage);
480 if(changed){
481 snapSetTimes(fsys->fs->snap, arch, snap, life);
482 return 1;
484 snapGetTimes(fsys->fs->snap, &arch, &snap, &life);
485 if(arch != ~(u32int)0)
486 sprint(buf, "-a %02d%02d", arch/60, arch%60);
487 else
488 sprint(buf, "-a none");
489 if(snap != ~(u32int)0)
490 sprint(buf+strlen(buf), " -s %d", snap);
491 else
492 sprint(buf+strlen(buf), " -s none");
493 if(life != ~(u32int)0)
494 sprint(buf+strlen(buf), " -t %ud", life);
495 else
496 sprint(buf+strlen(buf), " -t none");
497 consPrint("\tsnaptime %s\n", buf);
498 return 1;
501 static int
502 fsysSync(Fsys* fsys, int argc, char* argv[])
504 char *usage = "usage: [fsys name] sync";
505 int n;
507 ARGBEGIN{
508 default:
509 return cliError(usage);
510 }ARGEND
511 if(argc > 0)
512 return cliError(usage);
514 n = cacheDirty(fsys->fs->cache);
515 fsSync(fsys->fs);
516 consPrint("\t%s sync: wrote %d blocks\n", fsys->name, n);
517 return 1;
520 static int
521 fsysHalt(Fsys *fsys, int argc, char* argv[])
523 char *usage = "usage: [fsys name] halt";
525 ARGBEGIN{
526 default:
527 return cliError(usage);
528 }ARGEND
529 if(argc > 0)
530 return cliError(usage);
532 fsHalt(fsys->fs);
533 return 1;
536 static int
537 fsysUnhalt(Fsys *fsys, int argc, char* argv[])
539 char *usage = "usage: [fsys name] unhalt";
541 ARGBEGIN{
542 default:
543 return cliError(usage);
544 }ARGEND
545 if(argc > 0)
546 return cliError(usage);
548 if(!fsys->fs->halted)
549 return cliError("file system %s not halted", fsys->name);
551 fsUnhalt(fsys->fs);
552 return 1;
555 static int
556 fsysRemove(Fsys* fsys, int argc, char* argv[])
558 File *file;
559 char *usage = "usage: [fsys name] remove path ...";
561 ARGBEGIN{
562 default:
563 return cliError(usage);
564 }ARGEND
565 if(argc == 0)
566 return cliError(usage);
568 rlock(&fsys->fs->elk);
569 while(argc > 0){
570 if((file = fileOpen(fsys->fs, argv[0])) == nil)
571 consPrint("%s: %r\n", argv[0]);
572 else{
573 if(!fileRemove(file, uidadm))
574 consPrint("%s: %r\n", argv[0]);
575 fileDecRef(file);
577 argc--;
578 argv++;
580 runlock(&fsys->fs->elk);
582 return 1;
585 static int
586 fsysClri(Fsys* fsys, int argc, char* argv[])
588 char *usage = "usage: [fsys name] clri path ...";
590 ARGBEGIN{
591 default:
592 return cliError(usage);
593 }ARGEND
594 if(argc == 0)
595 return cliError(usage);
597 rlock(&fsys->fs->elk);
598 while(argc > 0){
599 if(!fileClriPath(fsys->fs, argv[0], uidadm))
600 consPrint("clri %s: %r\n", argv[0]);
601 argc--;
602 argv++;
604 runlock(&fsys->fs->elk);
606 return 1;
609 /*
610 * Inspect and edit the labels for blocks on disk.
611 */
612 static int
613 fsysLabel(Fsys* fsys, int argc, char* argv[])
615 Fs *fs;
616 Label l;
617 int n, r;
618 u32int addr;
619 Block *b, *bb;
620 char *usage = "usage: [fsys name] label addr [type state epoch epochClose tag]";
622 ARGBEGIN{
623 default:
624 return cliError(usage);
625 }ARGEND
626 if(argc != 1 && argc != 6)
627 return cliError(usage);
629 r = 0;
630 rlock(&fsys->fs->elk);
632 fs = fsys->fs;
633 addr = strtoul(argv[0], 0, 0);
634 b = cacheLocal(fs->cache, PartData, addr, OReadOnly);
635 if(b == nil)
636 goto Out0;
638 l = b->l;
639 consPrint("%slabel %#ux %ud %ud %ud %ud %#x\n",
640 argc==6 ? "old: " : "", addr, l.type, l.state,
641 l.epoch, l.epochClose, l.tag);
643 if(argc == 6){
644 if(strcmp(argv[1], "-") != 0)
645 l.type = atoi(argv[1]);
646 if(strcmp(argv[2], "-") != 0)
647 l.state = atoi(argv[2]);
648 if(strcmp(argv[3], "-") != 0)
649 l.epoch = strtoul(argv[3], 0, 0);
650 if(strcmp(argv[4], "-") != 0)
651 l.epochClose = strtoul(argv[4], 0, 0);
652 if(strcmp(argv[5], "-") != 0)
653 l.tag = strtoul(argv[5], 0, 0);
655 consPrint("new: label %#ux %ud %ud %ud %ud %#x\n",
656 addr, l.type, l.state, l.epoch, l.epochClose, l.tag);
657 bb = _blockSetLabel(b, &l);
658 if(bb == nil)
659 goto Out1;
660 n = 0;
661 for(;;){
662 if(blockWrite(bb, Waitlock)){
663 while(bb->iostate != BioClean){
664 assert(bb->iostate == BioWriting);
665 rsleep(&bb->ioready);
667 break;
669 consPrint("blockWrite: %r\n");
670 if(n++ >= 5){
671 consPrint("giving up\n");
672 break;
674 sleep(5*1000);
676 blockPut(bb);
678 r = 1;
679 Out1:
680 blockPut(b);
681 Out0:
682 runlock(&fs->elk);
684 return r;
687 /*
688 * Inspect and edit the blocks on disk.
689 */
690 static int
691 fsysBlock(Fsys* fsys, int argc, char* argv[])
693 Fs *fs;
694 char *s;
695 Block *b;
696 uchar *buf;
697 u32int addr;
698 int c, count, i, offset;
699 char *usage = "usage: [fsys name] block addr offset [count [data]]";
701 ARGBEGIN{
702 default:
703 return cliError(usage);
704 }ARGEND
705 if(argc < 2 || argc > 4)
706 return cliError(usage);
708 fs = fsys->fs;
709 addr = strtoul(argv[0], 0, 0);
710 offset = strtoul(argv[1], 0, 0);
711 if(offset < 0 || offset >= fs->blockSize){
712 werrstr("bad offset");
713 return 0;
715 if(argc > 2)
716 count = strtoul(argv[2], 0, 0);
717 else
718 count = 100000000;
719 if(offset+count > fs->blockSize)
720 count = fs->blockSize - count;
722 rlock(&fs->elk);
724 b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly);
725 if(b == nil){
726 werrstr("cacheLocal %#ux: %r", addr);
727 runlock(&fs->elk);
728 return 0;
731 consPrint("\t%sblock %#ux %ud %ud %.*H\n",
732 argc==4 ? "old: " : "", addr, offset, count, count, b->data+offset);
734 if(argc == 4){
735 s = argv[3];
736 if(strlen(s) != 2*count){
737 werrstr("bad data count");
738 goto Out;
740 buf = vtmallocz(count);
741 for(i = 0; i < count*2; i++){
742 if(s[i] >= '0' && s[i] <= '9')
743 c = s[i] - '0';
744 else if(s[i] >= 'a' && s[i] <= 'f')
745 c = s[i] - 'a' + 10;
746 else if(s[i] >= 'A' && s[i] <= 'F')
747 c = s[i] - 'A' + 10;
748 else{
749 werrstr("bad hex");
750 vtfree(buf);
751 goto Out;
753 if((i & 1) == 0)
754 c <<= 4;
755 buf[i>>1] |= c;
757 memmove(b->data+offset, buf, count);
758 consPrint("\tnew: block %#ux %ud %ud %.*H\n",
759 addr, offset, count, count, b->data+offset);
760 blockDirty(b);
763 Out:
764 blockPut(b);
765 runlock(&fs->elk);
767 return 1;
770 /*
771 * Free a disk block.
772 */
773 static int
774 fsysBfree(Fsys* fsys, int argc, char* argv[])
776 Fs *fs;
777 Label l;
778 char *p;
779 Block *b;
780 u32int addr;
781 char *usage = "usage: [fsys name] bfree addr ...";
783 ARGBEGIN{
784 default:
785 return cliError(usage);
786 }ARGEND
787 if(argc == 0)
788 return cliError(usage);
790 fs = fsys->fs;
791 rlock(&fs->elk);
792 while(argc > 0){
793 addr = strtoul(argv[0], &p, 0);
794 if(*p != '\0'){
795 consPrint("bad address - '%ud'\n", addr);
796 /* syntax error; let's stop */
797 runlock(&fs->elk);
798 return 0;
800 b = cacheLocal(fs->cache, PartData, addr, OReadOnly);
801 if(b == nil){
802 consPrint("loading %#ux: %r\n", addr);
803 continue;
805 l = b->l;
806 if(l.state == BsFree)
807 consPrint("%#ux is already free\n", addr);
808 else{
809 consPrint("label %#ux %ud %ud %ud %ud %#x\n",
810 addr, l.type, l.state, l.epoch, l.epochClose, l.tag);
811 l.state = BsFree;
812 l.type = BtMax;
813 l.tag = 0;
814 l.epoch = 0;
815 l.epochClose = 0;
816 if(!blockSetLabel(b, &l, 0))
817 consPrint("freeing %#ux: %r\n", addr);
819 blockPut(b);
820 argc--;
821 argv++;
823 runlock(&fs->elk);
825 return 1;
828 static int
829 fsysDf(Fsys *fsys, int argc, char* argv[])
831 char *usage = "usage: [fsys name] df";
832 u32int used, tot, bsize;
833 Fs *fs;
835 ARGBEGIN{
836 default:
837 return cliError(usage);
838 }ARGEND
839 if(argc != 0)
840 return cliError(usage);
842 fs = fsys->fs;
843 cacheCountUsed(fs->cache, fs->elo, &used, &tot, &bsize);
844 consPrint("\t%s: %,llud used + %,llud free = %,llud (%.1f%% used)\n",
845 fsys->name, used*(vlong)bsize, (tot-used)*(vlong)bsize,
846 tot*(vlong)bsize, used*100.0/tot);
847 return 1;
850 /*
851 * Zero an entry or a pointer.
852 */
853 static int
854 fsysClrep(Fsys* fsys, int argc, char* argv[], int ch)
856 Fs *fs;
857 Entry e;
858 Block *b;
859 u32int addr;
860 int i, max, offset, sz;
861 uchar zero[VtEntrySize];
862 char *usage = "usage: [fsys name] clr%c addr offset ...";
864 ARGBEGIN{
865 default:
866 return cliError(usage, ch);
867 }ARGEND
868 if(argc < 2)
869 return cliError(usage, ch);
871 fs = fsys->fs;
872 rlock(&fsys->fs->elk);
874 addr = strtoul(argv[0], 0, 0);
875 b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly);
876 if(b == nil){
877 werrstr("cacheLocal %#ux: %r", addr);
878 Err:
879 runlock(&fsys->fs->elk);
880 return 0;
883 switch(ch){
884 default:
885 werrstr("clrep");
886 goto Err;
887 case 'e':
888 if(b->l.type != BtDir){
889 werrstr("wrong block type");
890 goto Err;
892 sz = VtEntrySize;
893 memset(&e, 0, sizeof e);
894 entryPack(&e, zero, 0);
895 break;
896 case 'p':
897 if(b->l.type == BtDir || b->l.type == BtData){
898 werrstr("wrong block type");
899 goto Err;
901 sz = VtScoreSize;
902 memmove(zero, vtzeroscore, VtScoreSize);
903 break;
905 max = fs->blockSize/sz;
907 for(i = 1; i < argc; i++){
908 offset = atoi(argv[i]);
909 if(offset >= max){
910 consPrint("\toffset %d too large (>= %d)\n", i, max);
911 continue;
913 consPrint("\tblock %#ux %d %d %.*H\n", addr, offset*sz, sz, sz, b->data+offset*sz);
914 memmove(b->data+offset*sz, zero, sz);
916 blockDirty(b);
917 blockPut(b);
918 runlock(&fsys->fs->elk);
920 return 1;
923 static int
924 fsysClre(Fsys* fsys, int argc, char* argv[])
926 return fsysClrep(fsys, argc, argv, 'e');
929 static int
930 fsysClrp(Fsys* fsys, int argc, char* argv[])
932 return fsysClrep(fsys, argc, argv, 'p');
935 static int
936 fsysEsearch1(File* f, char* s, u32int elo)
938 int n, r;
939 DirEntry de;
940 DirEntryEnum *dee;
941 File *ff;
942 Entry e, ee;
943 char *t;
945 dee = deeOpen(f);
946 if(dee == nil)
947 return 0;
949 n = 0;
950 for(;;){
951 r = deeRead(dee, &de);
952 if(r < 0){
953 consPrint("\tdeeRead %s/%s: %r\n", s, de.elem);
954 break;
956 if(r == 0)
957 break;
958 if(de.mode & ModeSnapshot){
959 if((ff = fileWalk(f, de.elem)) == nil)
960 consPrint("\tcannot walk %s/%s: %r\n", s, de.elem);
961 else{
962 if(!fileGetSources(ff, &e, &ee))
963 consPrint("\tcannot get sources for %s/%s: %r\n", s, de.elem);
964 else if(e.snap != 0 && e.snap < elo){
965 consPrint("\t%ud\tclri %s/%s\n", e.snap, s, de.elem);
966 n++;
968 fileDecRef(ff);
971 else if(de.mode & ModeDir){
972 if((ff = fileWalk(f, de.elem)) == nil)
973 consPrint("\tcannot walk %s/%s: %r\n", s, de.elem);
974 else{
975 t = smprint("%s/%s", s, de.elem);
976 n += fsysEsearch1(ff, t, elo);
977 vtfree(t);
978 fileDecRef(ff);
981 deCleanup(&de);
982 if(r < 0)
983 break;
985 deeClose(dee);
987 return n;
990 static int
991 fsysEsearch(Fs* fs, char* path, u32int elo)
993 int n;
994 File *f;
995 DirEntry de;
997 f = fileOpen(fs, path);
998 if(f == nil)
999 return 0;
1000 if(!fileGetDir(f, &de)){
1001 consPrint("\tfileGetDir %s failed: %r\n", path);
1002 fileDecRef(f);
1003 return 0;
1005 if((de.mode & ModeDir) == 0){
1006 fileDecRef(f);
1007 deCleanup(&de);
1008 return 0;
1010 deCleanup(&de);
1011 n = fsysEsearch1(f, path, elo);
1012 fileDecRef(f);
1013 return n;
1016 static int
1017 fsysEpoch(Fsys* fsys, int argc, char* argv[])
1019 Fs *fs;
1020 int force, n, remove;
1021 u32int low, old;
1022 char *usage = "usage: [fsys name] epoch [[-ry] low]";
1024 force = 0;
1025 remove = 0;
1026 ARGBEGIN{
1027 case 'y':
1028 force = 1;
1029 break;
1030 case 'r':
1031 remove = 1;
1032 break;
1033 default:
1034 return cliError(usage);
1035 }ARGEND
1036 if(argc > 1)
1037 return cliError(usage);
1038 if(argc > 0)
1039 low = strtoul(argv[0], 0, 0);
1040 else
1041 low = ~(u32int)0;
1043 if(low == 0)
1044 return cliError("low epoch cannot be zero");
1046 fs = fsys->fs;
1048 rlock(&fs->elk);
1049 consPrint("\tlow %ud hi %ud\n", fs->elo, fs->ehi);
1050 if(low == ~(u32int)0){
1051 runlock(&fs->elk);
1052 return 1;
1054 n = fsysEsearch(fsys->fs, "/archive", low);
1055 n += fsysEsearch(fsys->fs, "/snapshot", low);
1056 consPrint("\t%d snapshot%s found with epoch < %ud\n", n, n==1 ? "" : "s", low);
1057 runlock(&fs->elk);
1060 * There's a small race here -- a new snapshot with epoch < low might
1061 * get introduced now that we unlocked fs->elk. Low has to
1062 * be <= fs->ehi. Of course, in order for this to happen low has
1063 * to be equal to the current fs->ehi _and_ a snapshot has to
1064 * run right now. This is a small enough window that I don't care.
1066 if(n != 0 && !force){
1067 consPrint("\tnot setting low epoch\n");
1068 return 1;
1070 old = fs->elo;
1071 if(!fsEpochLow(fs, low))
1072 consPrint("\tfsEpochLow: %r\n");
1073 else{
1074 consPrint("\told: epoch%s %ud\n", force ? " -y" : "", old);
1075 consPrint("\tnew: epoch%s %ud\n", force ? " -y" : "", fs->elo);
1076 if(fs->elo < low)
1077 consPrint("\twarning: new low epoch < old low epoch\n");
1078 if(force && remove)
1079 fsSnapshotRemove(fs);
1082 return 1;
1085 static int
1086 fsysCreate(Fsys* fsys, int argc, char* argv[])
1088 int r;
1089 ulong mode;
1090 char *elem, *p, *path;
1091 char *usage = "usage: [fsys name] create path uid gid perm";
1092 DirEntry de;
1093 File *file, *parent;
1095 ARGBEGIN{
1096 default:
1097 return cliError(usage);
1098 }ARGEND
1099 if(argc != 4)
1100 return cliError(usage);
1102 if(!fsysParseMode(argv[3], &mode))
1103 return cliError(usage);
1104 if(mode&ModeSnapshot)
1105 return cliError("create - cannot create with snapshot bit set");
1107 if(strcmp(argv[1], uidnoworld) == 0)
1108 return cliError("permission denied");
1110 rlock(&fsys->fs->elk);
1111 path = vtstrdup(argv[0]);
1112 if((p = strrchr(path, '/')) != nil){
1113 *p++ = '\0';
1114 elem = p;
1115 p = path;
1116 if(*p == '\0')
1117 p = "/";
1119 else{
1120 p = "/";
1121 elem = path;
1124 r = 0;
1125 if((parent = fileOpen(fsys->fs, p)) == nil)
1126 goto out;
1128 file = fileCreate(parent, elem, mode, argv[1]);
1129 fileDecRef(parent);
1130 if(file == nil){
1131 werrstr("create %s/%s: %r", p, elem);
1132 goto out;
1135 if(!fileGetDir(file, &de)){
1136 werrstr("stat failed after create: %r");
1137 goto out1;
1140 if(strcmp(de.gid, argv[2]) != 0){
1141 vtfree(de.gid);
1142 de.gid = vtstrdup(argv[2]);
1143 if(!fileSetDir(file, &de, argv[1])){
1144 werrstr("wstat failed after create: %r");
1145 goto out2;
1148 r = 1;
1150 out2:
1151 deCleanup(&de);
1152 out1:
1153 fileDecRef(file);
1154 out:
1155 vtfree(path);
1156 runlock(&fsys->fs->elk);
1158 return r;
1161 static void
1162 fsysPrintStat(char *prefix, char *file, DirEntry *de)
1164 char buf[64];
1166 if(prefix == nil)
1167 prefix = "";
1168 consPrint("%sstat %q %q %q %q %s %llud\n", prefix,
1169 file, de->elem, de->uid, de->gid, fsysModeString(de->mode, buf), de->size);
1172 static int
1173 fsysStat(Fsys* fsys, int argc, char* argv[])
1175 int i;
1176 File *f;
1177 DirEntry de;
1178 char *usage = "usage: [fsys name] stat files...";
1180 ARGBEGIN{
1181 default:
1182 return cliError(usage);
1183 }ARGEND
1185 if(argc == 0)
1186 return cliError(usage);
1188 rlock(&fsys->fs->elk);
1189 for(i=0; i<argc; i++){
1190 if((f = fileOpen(fsys->fs, argv[i])) == nil){
1191 consPrint("%s: %r\n", argv[i]);
1192 continue;
1194 if(!fileGetDir(f, &de)){
1195 consPrint("%s: %r\n", argv[i]);
1196 fileDecRef(f);
1197 continue;
1199 fsysPrintStat("\t", argv[i], &de);
1200 deCleanup(&de);
1201 fileDecRef(f);
1203 runlock(&fsys->fs->elk);
1204 return 1;
1207 static int
1208 fsysWstat(Fsys *fsys, int argc, char* argv[])
1210 File *f;
1211 char *p;
1212 DirEntry de;
1213 char *usage = "usage: [fsys name] wstat file elem uid gid mode length\n"
1214 "\tuse - for any field to mean don't change";
1216 ARGBEGIN{
1217 default:
1218 return cliError(usage);
1219 }ARGEND
1221 if(argc != 6)
1222 return cliError(usage);
1224 rlock(&fsys->fs->elk);
1225 if((f = fileOpen(fsys->fs, argv[0])) == nil){
1226 werrstr("console wstat - walk - %r");
1227 runlock(&fsys->fs->elk);
1228 return 0;
1230 if(!fileGetDir(f, &de)){
1231 werrstr("console wstat - stat - %r");
1232 fileDecRef(f);
1233 runlock(&fsys->fs->elk);
1234 return 0;
1236 fsysPrintStat("\told: w", argv[0], &de);
1238 if(strcmp(argv[1], "-") != 0){
1239 if(!validFileName(argv[1])){
1240 werrstr("console wstat - bad elem");
1241 goto error;
1243 vtfree(de.elem);
1244 de.elem = vtstrdup(argv[1]);
1246 if(strcmp(argv[2], "-") != 0){
1247 if(!validUserName(argv[2])){
1248 werrstr("console wstat - bad uid");
1249 goto error;
1251 vtfree(de.uid);
1252 de.uid = vtstrdup(argv[2]);
1254 if(strcmp(argv[3], "-") != 0){
1255 if(!validUserName(argv[3])){
1256 werrstr("console wstat - bad gid");
1257 goto error;
1259 vtfree(de.gid);
1260 de.gid = vtstrdup(argv[3]);
1262 if(strcmp(argv[4], "-") != 0){
1263 if(!fsysParseMode(argv[4], &de.mode)){
1264 werrstr("console wstat - bad mode");
1265 goto error;
1268 if(strcmp(argv[5], "-") != 0){
1269 de.size = strtoull(argv[5], &p, 0);
1270 if(argv[5][0] == '\0' || *p != '\0' || (vlong)de.size < 0){
1271 werrstr("console wstat - bad length");
1272 goto error;
1276 if(!fileSetDir(f, &de, uidadm)){
1277 werrstr("console wstat - %r");
1278 goto error;
1280 deCleanup(&de);
1282 if(!fileGetDir(f, &de)){
1283 werrstr("console wstat - stat2 - %r");
1284 goto error;
1286 fsysPrintStat("\tnew: w", argv[0], &de);
1287 deCleanup(&de);
1288 fileDecRef(f);
1289 runlock(&fsys->fs->elk);
1291 return 1;
1293 error:
1294 deCleanup(&de); /* okay to do this twice */
1295 fileDecRef(f);
1296 runlock(&fsys->fs->elk);
1297 return 0;
1300 static void
1301 fsckClri(Fsck *fsck, char *name, MetaBlock *mb, int i, Block *b)
1303 USED(name);
1305 if((fsck->flags&DoClri) == 0)
1306 return;
1308 mbDelete(mb, i);
1309 mbPack(mb);
1310 blockDirty(b);
1313 static void
1314 fsckClose(Fsck *fsck, Block *b, u32int epoch)
1316 Label l;
1318 if((fsck->flags&DoClose) == 0)
1319 return;
1320 l = b->l;
1321 if(l.state == BsFree || (l.state&BsClosed)){
1322 consPrint("%#ux is already closed\n", b->addr);
1323 return;
1325 if(epoch){
1326 l.state |= BsClosed;
1327 l.epochClose = epoch;
1328 }else
1329 l.state = BsFree;
1331 if(!blockSetLabel(b, &l, 0))
1332 consPrint("%#ux setlabel: %r\n", b->addr);
1335 static void
1336 fsckClre(Fsck *fsck, Block *b, int offset)
1338 Entry e;
1340 if((fsck->flags&DoClre) == 0)
1341 return;
1342 if(offset<0 || offset*VtEntrySize >= fsck->bsize){
1343 consPrint("bad clre\n");
1344 return;
1346 memset(&e, 0, sizeof e);
1347 entryPack(&e, b->data, offset);
1348 blockDirty(b);
1351 static void
1352 fsckClrp(Fsck *fsck, Block *b, int offset)
1354 if((fsck->flags&DoClrp) == 0)
1355 return;
1356 if(offset<0 || offset*VtScoreSize >= fsck->bsize){
1357 consPrint("bad clre\n");
1358 return;
1360 memmove(b->data+offset*VtScoreSize, vtzeroscore, VtScoreSize);
1361 blockDirty(b);
1364 static int
1365 fsysCheck(Fsys *fsys, int argc, char *argv[])
1367 int i, halting;
1368 char *usage = "usage: [fsys name] check [-v] [options]";
1369 Fsck fsck;
1370 Block *b;
1371 Super super;
1373 memset(&fsck, 0, sizeof fsck);
1374 fsck.fs = fsys->fs;
1375 fsck.clri = fsckClri;
1376 fsck.clre = fsckClre;
1377 fsck.clrp = fsckClrp;
1378 fsck.close = fsckClose;
1379 fsck.print = consPrint;
1381 ARGBEGIN{
1382 default:
1383 return cliError(usage);
1384 }ARGEND
1386 for(i=0; i<argc; i++){
1387 if(strcmp(argv[i], "pblock") == 0)
1388 fsck.printblocks = 1;
1389 else if(strcmp(argv[i], "pdir") == 0)
1390 fsck.printdirs = 1;
1391 else if(strcmp(argv[i], "pfile") == 0)
1392 fsck.printfiles = 1;
1393 else if(strcmp(argv[i], "bclose") == 0)
1394 fsck.flags |= DoClose;
1395 else if(strcmp(argv[i], "clri") == 0)
1396 fsck.flags |= DoClri;
1397 else if(strcmp(argv[i], "clre") == 0)
1398 fsck.flags |= DoClre;
1399 else if(strcmp(argv[i], "clrp") == 0)
1400 fsck.flags |= DoClrp;
1401 else if(strcmp(argv[i], "fix") == 0)
1402 fsck.flags |= DoClose|DoClri|DoClre|DoClrp;
1403 else if(strcmp(argv[i], "venti") == 0)
1404 fsck.useventi = 1;
1405 else if(strcmp(argv[i], "snapshot") == 0)
1406 fsck.walksnapshots = 1;
1407 else{
1408 consPrint("unknown option '%s'\n", argv[i]);
1409 return cliError(usage);
1413 halting = fsys->fs->halted==0;
1414 if(halting)
1415 fsHalt(fsys->fs);
1416 if(fsys->fs->arch){
1417 b = superGet(fsys->fs->cache, &super);
1418 if(b == nil){
1419 consPrint("could not load super block\n");
1420 goto Out;
1422 blockPut(b);
1423 if(super.current != NilBlock){
1424 consPrint("cannot check fs while archiver is running; "
1425 "wait for it to finish\n");
1426 goto Out;
1429 fsCheck(&fsck);
1430 consPrint("fsck: %d clri, %d clre, %d clrp, %d bclose\n",
1431 fsck.nclri, fsck.nclre, fsck.nclrp, fsck.nclose);
1432 Out:
1433 if(halting)
1434 fsUnhalt(fsys->fs);
1435 return 1;
1438 static int
1439 fsysVenti(char* name, int argc, char* argv[])
1441 int r;
1442 char *host;
1443 char *usage = "usage: [fsys name] venti [address]";
1444 Fsys *fsys;
1446 ARGBEGIN{
1447 default:
1448 return cliError(usage);
1449 }ARGEND
1451 if(argc == 0)
1452 host = nil;
1453 else if(argc == 1)
1454 host = argv[0];
1455 else
1456 return cliError(usage);
1458 if((fsys = _fsysGet(name)) == nil)
1459 return 0;
1461 qlock(&fsys->lock);
1462 if(host == nil)
1463 host = fsys->venti;
1464 else{
1465 vtfree(fsys->venti);
1466 if(host[0])
1467 fsys->venti = vtstrdup(host);
1468 else{
1469 host = nil;
1470 fsys->venti = nil;
1474 /* already open: do a redial */
1475 if(fsys->fs != nil){
1476 if(fsys->session == nil){
1477 werrstr("file system was opened with -V");
1478 r = 0;
1479 goto out;
1481 r = 1;
1482 if(myRedial(fsys->session, host) < 0
1483 || vtconnect(fsys->session) < 0)
1484 r = 0;
1485 goto out;
1488 /* not yet open: try to dial */
1489 if(fsys->session)
1490 vtfreeconn(fsys->session);
1491 r = 1;
1492 if((fsys->session = myDial(host)) == nil
1493 || vtconnect(fsys->session) < 0)
1494 r = 0;
1495 out:
1496 qunlock(&fsys->lock);
1497 fsysPut(fsys);
1498 return r;
1501 static ulong
1502 freemem(void)
1504 int nf, pgsize = 0;
1505 uvlong size, userpgs = 0, userused = 0;
1506 char *ln, *sl;
1507 char *fields[2];
1508 Biobuf *bp;
1510 size = 64*1024*1024;
1511 bp = Bopen("#c/swap", OREAD);
1512 if (bp != nil) {
1513 while ((ln = Brdline(bp, '\n')) != nil) {
1514 ln[Blinelen(bp)-1] = '\0';
1515 nf = tokenize(ln, fields, nelem(fields));
1516 if (nf != 2)
1517 continue;
1518 if (strcmp(fields[1], "pagesize") == 0)
1519 pgsize = atoi(fields[0]);
1520 else if (strcmp(fields[1], "user") == 0) {
1521 sl = strchr(fields[0], '/');
1522 if (sl == nil)
1523 continue;
1524 userpgs = atoll(sl+1);
1525 userused = atoll(fields[0]);
1528 Bterm(bp);
1529 if (pgsize > 0 && userpgs > 0)
1530 size = (userpgs - userused) * pgsize;
1532 /* cap it to keep the size within 32 bits */
1533 if (size >= 3840UL * 1024 * 1024)
1534 size = 3840UL * 1024 * 1024;
1535 return size;
1538 static int
1539 fsysOpen(char* name, int argc, char* argv[])
1541 char *p, *host;
1542 Fsys *fsys;
1543 int noauth, noventi, noperm, rflag, wstatallow, noatimeupd;
1544 long ncache;
1545 char *usage = "usage: fsys name open [-APVWr] [-c ncache]";
1547 ncache = 1000;
1548 noauth = noperm = wstatallow = noventi = noatimeupd = 0;
1549 rflag = OReadWrite;
1551 ARGBEGIN{
1552 default:
1553 return cliError(usage);
1554 case 'A':
1555 noauth = 1;
1556 break;
1557 case 'P':
1558 noperm = 1;
1559 break;
1560 case 'V':
1561 noventi = 1;
1562 break;
1563 case 'W':
1564 wstatallow = 1;
1565 break;
1566 case 'a':
1567 noatimeupd = 1;
1568 break;
1569 case 'c':
1570 p = ARGF();
1571 if(p == nil)
1572 return cliError(usage);
1573 ncache = strtol(argv[0], &p, 0);
1574 if(ncache <= 0 || p == argv[0] || *p != '\0')
1575 return cliError(usage);
1576 break;
1577 case 'r':
1578 rflag = OReadOnly;
1579 break;
1580 }ARGEND
1581 if(argc)
1582 return cliError(usage);
1584 if((fsys = _fsysGet(name)) == nil)
1585 return 0;
1587 /* automatic memory sizing? */
1588 if(mempcnt > 0) {
1589 /* TODO: 8K is a hack; use the actual block size */
1590 ncache = (((vlong)freemem() * mempcnt) / 100) / (8*1024);
1591 if (ncache < 100)
1592 ncache = 100;
1595 qlock(&fsys->lock);
1596 if(fsys->fs != nil){
1597 werrstr(EFsysBusy, fsys->name);
1598 qunlock(&fsys->lock);
1599 fsysPut(fsys);
1600 return 0;
1603 if(noventi){
1604 if(fsys->session){
1605 vtfreeconn(fsys->session);
1606 fsys->session = nil;
1609 else if(fsys->session == nil){
1610 if(fsys->venti && fsys->venti[0])
1611 host = fsys->venti;
1612 else
1613 host = nil;
1615 if((fsys->session = myDial(host)) == nil
1616 || vtconnect(fsys->session) < 0 && !noventi)
1617 fprint(2, "warning: connecting to venti: %r\n");
1619 if((fsys->fs = fsOpen(fsys->dev, fsys->session, ncache, rflag)) == nil){
1620 werrstr("fsOpen: %r");
1621 qunlock(&fsys->lock);
1622 fsysPut(fsys);
1623 return 0;
1625 fsys->fs->name = fsys->name; /* for better error messages */
1626 fsys->noauth = noauth;
1627 fsys->noperm = noperm;
1628 fsys->wstatallow = wstatallow;
1629 fsys->fs->noatimeupd = noatimeupd;
1630 qunlock(&fsys->lock);
1631 fsysPut(fsys);
1633 if(strcmp(name, "main") == 0)
1634 usersFileRead(nil);
1636 return 1;
1639 static int
1640 fsysUnconfig(char* name, int argc, char* argv[])
1642 Fsys *fsys, **fp;
1643 char *usage = "usage: fsys name unconfig";
1645 ARGBEGIN{
1646 default:
1647 return cliError(usage);
1648 }ARGEND
1649 if(argc)
1650 return cliError(usage);
1652 wlock(&sbox.lock);
1653 fp = &sbox.head;
1654 for(fsys = *fp; fsys != nil; fsys = fsys->next){
1655 if(strcmp(fsys->name, name) == 0)
1656 break;
1657 fp = &fsys->next;
1659 if(fsys == nil){
1660 werrstr(EFsysNotFound, name);
1661 wunlock(&sbox.lock);
1662 return 0;
1664 if(fsys->ref != 0 || fsys->fs != nil){
1665 werrstr(EFsysBusy, fsys->name);
1666 wunlock(&sbox.lock);
1667 return 0;
1669 *fp = fsys->next;
1670 wunlock(&sbox.lock);
1672 if(fsys->session != nil)
1673 vtfreeconn(fsys->session);
1674 if(fsys->venti != nil)
1675 vtfree(fsys->venti);
1676 if(fsys->dev != nil)
1677 vtfree(fsys->dev);
1678 if(fsys->name != nil)
1679 vtfree(fsys->name);
1680 vtfree(fsys);
1682 return 1;
1685 static int
1686 fsysConfig(char* name, int argc, char* argv[])
1688 Fsys *fsys;
1689 char *part;
1690 char *usage = "usage: fsys name config [dev]";
1692 ARGBEGIN{
1693 default:
1694 return cliError(usage);
1695 }ARGEND
1696 if(argc > 1)
1697 return cliError(usage);
1699 if(argc == 0)
1700 part = foptname;
1701 else
1702 part = argv[0];
1704 if((fsys = _fsysGet(part)) != nil){
1705 qlock(&fsys->lock);
1706 if(fsys->fs != nil){
1707 werrstr(EFsysBusy, fsys->name);
1708 qunlock(&fsys->lock);
1709 fsysPut(fsys);
1710 return 0;
1712 vtfree(fsys->dev);
1713 fsys->dev = vtstrdup(part);
1714 qunlock(&fsys->lock);
1716 else if((fsys = fsysAlloc(name, part)) == nil)
1717 return 0;
1719 fsysPut(fsys);
1720 return 1;
1723 static struct {
1724 char* cmd;
1725 int (*f)(Fsys*, int, char**);
1726 int (*f1)(char*, int, char**);
1727 } fsyscmd[] = {
1728 { "close", fsysClose, },
1729 { "config", nil, fsysConfig, },
1730 { "open", nil, fsysOpen, },
1731 { "unconfig", nil, fsysUnconfig, },
1732 { "venti", nil, fsysVenti, },
1734 { "bfree", fsysBfree, },
1735 { "block", fsysBlock, },
1736 { "check", fsysCheck, },
1737 { "clre", fsysClre, },
1738 { "clri", fsysClri, },
1739 { "clrp", fsysClrp, },
1740 { "create", fsysCreate, },
1741 { "df", fsysDf, },
1742 { "epoch", fsysEpoch, },
1743 { "halt", fsysHalt, },
1744 { "label", fsysLabel, },
1745 { "remove", fsysRemove, },
1746 { "snap", fsysSnap, },
1747 { "snaptime", fsysSnapTime, },
1748 { "snapclean", fsysSnapClean, },
1749 { "stat", fsysStat, },
1750 { "sync", fsysSync, },
1751 { "unhalt", fsysUnhalt, },
1752 { "wstat", fsysWstat, },
1753 { "vac", fsysVac, },
1755 { nil, nil, },
1758 static int
1759 fsysXXX1(Fsys *fsys, int i, int argc, char* argv[])
1761 int r;
1763 qlock(&fsys->lock);
1764 if(fsys->fs == nil){
1765 qunlock(&fsys->lock);
1766 werrstr(EFsysNotOpen, fsys->name);
1767 return 0;
1770 if(fsys->fs->halted
1771 && fsyscmd[i].f != fsysUnhalt && fsyscmd[i].f != fsysCheck){
1772 werrstr("file system %s is halted", fsys->name);
1773 qunlock(&fsys->lock);
1774 return 0;
1777 r = (*fsyscmd[i].f)(fsys, argc, argv);
1778 qunlock(&fsys->lock);
1779 return r;
1782 static int
1783 fsysXXX(char* name, int argc, char* argv[])
1785 int i, r;
1786 Fsys *fsys;
1788 for(i = 0; fsyscmd[i].cmd != nil; i++){
1789 if(strcmp(fsyscmd[i].cmd, argv[0]) == 0)
1790 break;
1793 if(fsyscmd[i].cmd == nil){
1794 werrstr("unknown command - '%s'", argv[0]);
1795 return 0;
1798 /* some commands want the name... */
1799 if(fsyscmd[i].f1 != nil){
1800 if(strcmp(name, FsysAll) == 0){
1801 werrstr("cannot use fsys %#q with %#q command", FsysAll, argv[0]);
1802 return 0;
1804 return (*fsyscmd[i].f1)(name, argc, argv);
1807 /* ... but most commands want the Fsys */
1808 if(strcmp(name, FsysAll) == 0){
1809 r = 1;
1810 rlock(&sbox.lock);
1811 for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
1812 fsys->ref++;
1813 r = fsysXXX1(fsys, i, argc, argv) && r;
1814 fsys->ref--;
1816 runlock(&sbox.lock);
1817 }else{
1818 if((fsys = _fsysGet(name)) == nil)
1819 return 0;
1820 r = fsysXXX1(fsys, i, argc, argv);
1821 fsysPut(fsys);
1823 return r;
1826 static int
1827 cmdFsysXXX(int argc, char* argv[])
1829 char *name;
1831 if((name = sbox.curfsys) == nil){
1832 werrstr(EFsysNoCurrent, argv[0]);
1833 return 0;
1836 return fsysXXX(name, argc, argv);
1839 static int
1840 cmdFsys(int argc, char* argv[])
1842 Fsys *fsys;
1843 char *usage = "usage: fsys [name ...]";
1845 ARGBEGIN{
1846 default:
1847 return cliError(usage);
1848 }ARGEND
1850 if(argc == 0){
1851 rlock(&sbox.lock);
1852 currfsysname = sbox.head->name;
1853 for(fsys = sbox.head; fsys != nil; fsys = fsys->next)
1854 consPrint("\t%s\n", fsys->name);
1855 runlock(&sbox.lock);
1856 return 1;
1858 if(argc == 1){
1859 fsys = nil;
1860 if(strcmp(argv[0], FsysAll) != 0 && (fsys = fsysGet(argv[0])) == nil)
1861 return 0;
1862 sbox.curfsys = vtstrdup(argv[0]);
1863 consPrompt(sbox.curfsys);
1864 if(fsys)
1865 fsysPut(fsys);
1866 return 1;
1869 return fsysXXX(argv[0], argc-1, argv+1);
1872 int
1873 fsysInit(void)
1875 int i;
1877 fmtinstall('H', encodefmt);
1878 fmtinstall('V', scoreFmt);
1879 fmtinstall('L', labelFmt);
1881 cliAddCmd("fsys", cmdFsys);
1882 for(i = 0; fsyscmd[i].cmd != nil; i++){
1883 if(fsyscmd[i].f != nil)
1884 cliAddCmd(fsyscmd[i].cmd, cmdFsysXXX);
1886 /* the venti cmd is special: the fs can be either open or closed */
1887 cliAddCmd("venti", cmdFsysXXX);
1888 cliAddCmd("printconfig", cmdPrintConfig);
1890 return 1;