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