xref: /plan9-contrib/sys/src/cmd/fossil/9fsys.c (revision 4d44ba9b9ee4246ddbd96c7fcaf0918ab92ab35a)
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[0]);
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 		if(l.state == BsFree)
765 			consPrint("%#ux is already free\n", addr);
766 		else{
767 			consPrint("label %#ux %ud %ud %ud %ud %#x\n",
768 				addr, l.type, l.state, l.epoch, l.epochClose, l.tag);
769 			l.state = BsFree;
770 			l.type = BtMax;
771 			l.tag = 0;
772 			l.epoch = 0;
773 			l.epochClose = 0;
774 			if(!blockSetLabel(b, &l, 0))
775 				consPrint("freeing %#ux: %R\n", addr);
776 		}
777 		blockPut(b);
778 		argc--;
779 		argv++;
780 	}
781 	vtRUnlock(fs->elk);
782 
783 	return 1;
784 }
785 
786 static int
787 fsysDf(Fsys *fsys, int argc, char* argv[])
788 {
789 	char *usage = "usage: [fsys name] df";
790 	u32int used, tot, bsize;
791 	Fs *fs;
792 
793 	ARGBEGIN{
794 	default:
795 		return cliError(usage);
796 	}ARGEND
797 	if(argc != 0)
798 		return cliError(usage);
799 
800 	fs = fsys->fs;
801 	cacheCountUsed(fs->cache, fs->elo, &used, &tot, &bsize);
802 	consPrint("\t%s: %,llud used + %,llud free = %,llud (%ud%% used)\n",
803 		fsys->name, used*(vlong)bsize, (tot-used)*(vlong)bsize,
804 		tot*(vlong)bsize, used*100/tot);
805 	return 1;
806 }
807 
808 /*
809  * Zero an entry or a pointer.
810  */
811 static int
812 fsysClrep(Fsys* fsys, int argc, char* argv[], int ch)
813 {
814 	Fs *fs;
815 	Entry e;
816 	Block *b;
817 	u32int addr;
818 	int i, max, offset, sz;
819 	uchar zero[VtEntrySize];
820 	char *usage = "usage: [fsys name] clr%c addr offset ...";
821 
822 	ARGBEGIN{
823 	default:
824 		return cliError(usage, ch);
825 	}ARGEND
826 	if(argc < 2)
827 		return cliError(usage, ch);
828 
829 	fs = fsys->fs;
830 	vtRLock(fsys->fs->elk);
831 
832 	addr = strtoul(argv[0], 0, 0);
833 	b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly);
834 	if(b == nil){
835 		vtSetError("cacheLocal %#ux: %R", addr);
836 	Err:
837 		vtRUnlock(fsys->fs->elk);
838 		return 0;
839 	}
840 
841 	switch(ch){
842 	default:
843 		vtSetError("clrep");
844 		goto Err;
845 	case 'e':
846 		if(b->l.type != BtDir){
847 			vtSetError("wrong block type");
848 			goto Err;
849 		}
850 		sz = VtEntrySize;
851 		memset(&e, 0, sizeof e);
852 		entryPack(&e, zero, 0);
853 		break;
854 	case 'p':
855 		if(b->l.type == BtDir || b->l.type == BtData){
856 			vtSetError("wrong block type");
857 			goto Err;
858 		}
859 		sz = VtScoreSize;
860 		memmove(zero, vtZeroScore, VtScoreSize);
861 		break;
862 	}
863 	max = fs->blockSize/sz;
864 
865 	for(i = 1; i < argc; i++){
866 		offset = atoi(argv[i]);
867 		if(offset >= max){
868 			consPrint("\toffset %d too large (>= %d)\n", i, max);
869 			continue;
870 		}
871 		consPrint("\tblock %#ux %d %d %.*H\n", addr, offset*sz, sz, sz, b->data+offset*sz);
872 		memmove(b->data+offset*sz, zero, sz);
873 	}
874 	blockDirty(b);
875 	blockPut(b);
876 	vtRUnlock(fsys->fs->elk);
877 
878 	return 1;
879 }
880 
881 static int
882 fsysClre(Fsys* fsys, int argc, char* argv[])
883 {
884 	return fsysClrep(fsys, argc, argv, 'e');
885 }
886 
887 static int
888 fsysClrp(Fsys* fsys, int argc, char* argv[])
889 {
890 	return fsysClrep(fsys, argc, argv, 'p');
891 }
892 
893 static int
894 fsysEsearch1(File* f, char* s, u32int elo)
895 {
896 	int n, r;
897 	DirEntry de;
898 	DirEntryEnum *dee;
899 	File *ff;
900 	Entry e, ee;
901 	char *t;
902 
903 	dee = deeOpen(f);
904 	if(dee == nil)
905 		return 0;
906 
907 	n = 0;
908 	for(;;){
909 		r = deeRead(dee, &de);
910 		if(r < 0){
911 			consPrint("\tdeeRead %s/%s: %R\n", s, de.elem);
912 			break;
913 		}
914 		if(r == 0)
915 			break;
916 		if(de.mode & ModeSnapshot){
917 			if((ff = fileWalk(f, de.elem)) == nil)
918 				consPrint("\tcannot walk %s/%s: %R\n", s, de.elem);
919 			else{
920 				if(!fileGetSources(ff, &e, &ee))
921 					consPrint("\tcannot get sources for %s/%s: %R\n", s, de.elem);
922 				else if(e.snap != 0 && e.snap < elo){
923 					consPrint("\t%ud\tclri %s/%s\n", e.snap, s, de.elem);
924 					n++;
925 				}
926 				fileDecRef(ff);
927 			}
928 		}
929 		else if(de.mode & ModeDir){
930 			if((ff = fileWalk(f, de.elem)) == nil)
931 				consPrint("\tcannot walk %s/%s: %R\n", s, de.elem);
932 			else{
933 				t = smprint("%s/%s", s, de.elem);
934 				n += fsysEsearch1(ff, t, elo);
935 				vtMemFree(t);
936 				fileDecRef(ff);
937 			}
938 		}
939 		deCleanup(&de);
940 		if(r < 0)
941 			break;
942 	}
943 	deeClose(dee);
944 
945 	return n;
946 }
947 
948 static int
949 fsysEsearch(Fs* fs, char* path, u32int elo)
950 {
951 	int n;
952 	File *f;
953 	DirEntry de;
954 
955 	f = fileOpen(fs, path);
956 	if(f == nil)
957 		return 0;
958 	if(!fileGetDir(f, &de)){
959 		consPrint("\tfileGetDir %s failed: %R\n", path);
960 		fileDecRef(f);
961 		return 0;
962 	}
963 	if((de.mode & ModeDir) == 0){
964 		fileDecRef(f);
965 		deCleanup(&de);
966 		return 0;
967 	}
968 	deCleanup(&de);
969 	n = fsysEsearch1(f, path, elo);
970 	fileDecRef(f);
971 	return n;
972 }
973 
974 static int
975 fsysEpoch(Fsys* fsys, int argc, char* argv[])
976 {
977 	Fs *fs;
978 	int force, n, remove;
979 	u32int low, old;
980 	char *usage = "usage: [fsys name] epoch [[-ry] low]";
981 
982 	force = 0;
983 	remove = 0;
984 	ARGBEGIN{
985 	case 'y':
986 		force = 1;
987 		break;
988 	case 'r':
989 		remove = 1;
990 		break;
991 	default:
992 		return cliError(usage);
993 	}ARGEND
994 	if(argc > 1)
995 		return cliError(usage);
996 	if(argc > 0)
997 		low = strtoul(argv[0], 0, 0);
998 	else
999 		low = ~(u32int)0;
1000 
1001 	if(low == 0)
1002 		return cliError("low epoch cannot be zero");
1003 
1004 	fs = fsys->fs;
1005 
1006 	vtRLock(fs->elk);
1007 	consPrint("\tlow %ud hi %ud\n", fs->elo, fs->ehi);
1008 	n = fsysEsearch(fsys->fs, "/archive", low);
1009 	n += fsysEsearch(fsys->fs, "/snapshot", low);
1010 	consPrint("\t%d snapshot%s found with epoch < %ud\n", n, n==1 ? "" : "s", low);
1011 	vtRUnlock(fs->elk);
1012 
1013 	/*
1014 	 * There's a small race here -- a new snapshot with epoch < low might
1015 	 * get introduced now that we unlocked fs->elk.  Low has to
1016 	 * be <= fs->ehi.  Of course, in order for this to happen low has
1017 	 * to be equal to the current fs->ehi _and_ a snapshot has to
1018 	 * run right now.  This is a small enough window that I don't care.
1019 	 */
1020 	if(n != 0 && !force){
1021 		consPrint("\tnot setting low epoch\n");
1022 		return 1;
1023 	}
1024 	old = fs->elo;
1025 	if(!fsEpochLow(fs, low))
1026 		consPrint("\tfsEpochLow: %R\n");
1027 	else{
1028 		consPrint("\told: epoch%s %ud\n", force ? " -y" : "", old);
1029 		consPrint("\tnew: epoch%s %ud\n", force ? " -y" : "", fs->elo);
1030 		if(fs->elo < low)
1031 			consPrint("\twarning: new low epoch < old low epoch\n");
1032 		if(force && remove)
1033 			fsSnapshotRemove(fs);
1034 	}
1035 
1036 	return 1;
1037 }
1038 
1039 static int
1040 fsysCreate(Fsys* fsys, int argc, char* argv[])
1041 {
1042 	int r;
1043 	ulong mode;
1044 	char *elem, *p, *path;
1045 	char *usage = "usage: [fsys name] create path uid gid perm";
1046 	DirEntry de;
1047 	File *file, *parent;
1048 
1049 	ARGBEGIN{
1050 	default:
1051 		return cliError(usage);
1052 	}ARGEND
1053 	if(argc != 4)
1054 		return cliError(usage);
1055 
1056 	if(!fsysParseMode(argv[3], &mode))
1057 		return cliError(usage);
1058 	if(mode&ModeSnapshot)
1059 		return cliError("create - cannot create with snapshot bit set");
1060 
1061 	if(strcmp(argv[1], uidnoworld) == 0)
1062 		return cliError("permission denied");
1063 
1064 	vtRLock(fsys->fs->elk);
1065 	path = vtStrDup(argv[0]);
1066 	if((p = strrchr(path, '/')) != nil){
1067 		*p++ = '\0';
1068 		elem = p;
1069 		p = path;
1070 		if(*p == '\0')
1071 			p = "/";
1072 	}
1073 	else{
1074 		p = "/";
1075 		elem = path;
1076 	}
1077 
1078 	r = 0;
1079 	if((parent = fileOpen(fsys->fs, p)) == nil)
1080 		goto out;
1081 
1082 	file = fileCreate(parent, elem, mode, argv[1]);
1083 	fileDecRef(parent);
1084 	if(file == nil){
1085 		vtSetError("create %s/%s: %R", p, elem);
1086 		goto out;
1087 	}
1088 
1089 	if(!fileGetDir(file, &de)){
1090 		vtSetError("stat failed after create: %R");
1091 		goto out1;
1092 	}
1093 
1094 	if(strcmp(de.gid, argv[2]) != 0){
1095 		vtMemFree(de.gid);
1096 		de.gid = vtStrDup(argv[2]);
1097 		if(!fileSetDir(file, &de, argv[1])){
1098 			vtSetError("wstat failed after create: %R");
1099 			goto out2;
1100 		}
1101 	}
1102 	r = 1;
1103 
1104 out2:
1105 	deCleanup(&de);
1106 out1:
1107 	fileDecRef(file);
1108 out:
1109 	vtMemFree(path);
1110 	vtRUnlock(fsys->fs->elk);
1111 
1112 	return r;
1113 }
1114 
1115 static void
1116 fsysPrintStat(char *prefix, char *file, DirEntry *de)
1117 {
1118 	char buf[64];
1119 
1120 	if(prefix == nil)
1121 		prefix = "";
1122 	consPrint("%sstat %q %q %q %q %s %llud\n", prefix,
1123 		file, de->elem, de->uid, de->gid, fsysModeString(de->mode, buf), de->size);
1124 }
1125 
1126 static int
1127 fsysStat(Fsys* fsys, int argc, char* argv[])
1128 {
1129 	int i;
1130 	File *f;
1131 	DirEntry de;
1132 	char *usage = "usage: [fsys name] stat files...";
1133 
1134 	ARGBEGIN{
1135 	default:
1136 		return cliError(usage);
1137 	}ARGEND
1138 
1139 	if(argc == 0)
1140 		return cliError(usage);
1141 
1142 	vtRLock(fsys->fs->elk);
1143 	for(i=0; i<argc; i++){
1144 		if((f = fileOpen(fsys->fs, argv[i])) == nil){
1145 			consPrint("%s: %R\n");
1146 			continue;
1147 		}
1148 		if(!fileGetDir(f, &de)){
1149 			consPrint("%s: %R\n");
1150 			fileDecRef(f);
1151 			continue;
1152 		}
1153 		fsysPrintStat("\t", argv[i], &de);
1154 		deCleanup(&de);
1155 		fileDecRef(f);
1156 	}
1157 	vtRUnlock(fsys->fs->elk);
1158 	return 1;
1159 }
1160 
1161 static int
1162 fsysWstat(Fsys *fsys, int argc, char* argv[])
1163 {
1164 	File *f;
1165 	char *p;
1166 	DirEntry de;
1167 	char *usage = "usage: [fsys name] wstat file elem uid gid mode length\n"
1168 		"\tuse - for any field to mean don't change";
1169 
1170 	ARGBEGIN{
1171 	default:
1172 		return cliError(usage);
1173 	}ARGEND
1174 
1175 	if(argc != 6)
1176 		return cliError(usage);
1177 
1178 	vtRLock(fsys->fs->elk);
1179 	if((f = fileOpen(fsys->fs, argv[0])) == nil){
1180 		vtSetError("console wstat - walk - %R");
1181 		vtRUnlock(fsys->fs->elk);
1182 		return 0;
1183 	}
1184 	if(!fileGetDir(f, &de)){
1185 		vtSetError("console wstat - stat - %R");
1186 		fileDecRef(f);
1187 		vtRUnlock(fsys->fs->elk);
1188 		return 0;
1189 	}
1190 	fsysPrintStat("\told: w", argv[0], &de);
1191 
1192 	if(strcmp(argv[1], "-") != 0){
1193 		if(!validFileName(argv[1])){
1194 			vtSetError("console wstat - bad elem");
1195 			goto error;
1196 		}
1197 		vtMemFree(de.elem);
1198 		de.elem = vtStrDup(argv[1]);
1199 	}
1200 	if(strcmp(argv[2], "-") != 0){
1201 		if(!validUserName(argv[2])){
1202 			vtSetError("console wstat - bad uid");
1203 			goto error;
1204 		}
1205 		vtMemFree(de.uid);
1206 		de.uid = vtStrDup(argv[2]);
1207 	}
1208 	if(strcmp(argv[3], "-") != 0){
1209 		if(!validUserName(argv[3])){
1210 			vtSetError("console wstat - bad gid");
1211 			goto error;
1212 		}
1213 		vtMemFree(de.gid);
1214 		de.gid = vtStrDup(argv[3]);
1215 	}
1216 	if(strcmp(argv[4], "-") != 0){
1217 		if(!fsysParseMode(argv[4], &de.mode)){
1218 			vtSetError("console wstat - bad mode");
1219 			goto error;
1220 		}
1221 	}
1222 	if(strcmp(argv[5], "-") != 0){
1223 		de.size = strtoull(argv[5], &p, 0);
1224 		if(argv[5][0] == '\0' || *p != '\0' || de.size < 0){
1225 			vtSetError("console wstat - bad length");
1226 			goto error;
1227 		}
1228 	}
1229 
1230 	if(!fileSetDir(f, &de, uidadm)){
1231 		vtSetError("console wstat - %R");
1232 		goto error;
1233 	}
1234 	deCleanup(&de);
1235 
1236 	if(!fileGetDir(f, &de)){
1237 		vtSetError("console wstat - stat2 - %R");
1238 		goto error;
1239 	}
1240 	fsysPrintStat("\tnew: w", argv[0], &de);
1241 	deCleanup(&de);
1242 	fileDecRef(f);
1243 	vtRUnlock(fsys->fs->elk);
1244 
1245 	return 1;
1246 
1247 error:
1248 	deCleanup(&de);	/* okay to do this twice */
1249 	fileDecRef(f);
1250 	vtRUnlock(fsys->fs->elk);
1251 	return 0;
1252 }
1253 
1254 static void
1255 fsckClri(Fsck *fsck, char *name, MetaBlock *mb, int i, Block *b)
1256 {
1257 	USED(name);
1258 
1259 	if((fsck->flags&DoClri) == 0)
1260 		return;
1261 
1262 	mbDelete(mb, i);
1263 	mbPack(mb);
1264 	blockDirty(b);
1265 }
1266 
1267 static void
1268 fsckClose(Fsck *fsck, Block *b, u32int epoch)
1269 {
1270 	Label l;
1271 
1272 	if((fsck->flags&DoClose) == 0)
1273 		return;
1274 	l = b->l;
1275 	if(l.state == BsFree || (l.state&BsClosed)){
1276 		consPrint("%#ux is already closed\n", b->addr);
1277 		return;
1278 	}
1279 	if(epoch){
1280 		l.state |= BsClosed;
1281 		l.epochClose = epoch;
1282 	}else
1283 		l.state = BsFree;
1284 
1285 	if(!blockSetLabel(b, &l, 0))
1286 		consPrint("%#ux setlabel: %R\n", b->addr);
1287 }
1288 
1289 static void
1290 fsckClre(Fsck *fsck, Block *b, int offset)
1291 {
1292 	Entry e;
1293 
1294 	if((fsck->flags&DoClre) == 0)
1295 		return;
1296 	if(offset<0 || offset*VtEntrySize >= fsck->bsize){
1297 		consPrint("bad clre\n");
1298 		return;
1299 	}
1300 	memset(&e, 0, sizeof e);
1301 	entryPack(&e, b->data, offset);
1302 	blockDirty(b);
1303 }
1304 
1305 static void
1306 fsckClrp(Fsck *fsck, Block *b, int offset)
1307 {
1308 	if((fsck->flags&DoClrp) == 0)
1309 		return;
1310 	if(offset<0 || offset*VtScoreSize >= fsck->bsize){
1311 		consPrint("bad clre\n");
1312 		return;
1313 	}
1314 	memmove(b->data+offset*VtScoreSize, vtZeroScore, VtScoreSize);
1315 	blockDirty(b);
1316 }
1317 
1318 static int
1319 fsysCheck(Fsys *fsys, int argc, char *argv[])
1320 {
1321 	int i, halting;
1322 	char *usage = "usage: [fsys name] check [-v] [options]";
1323 	Fsck fsck;
1324 	Block *b;
1325 	Super super;
1326 
1327 	memset(&fsck, 0, sizeof fsck);
1328 	fsck.fs = fsys->fs;
1329 	fsck.clri = fsckClri;
1330 	fsck.clre = fsckClre;
1331 	fsck.clrp = fsckClrp;
1332 	fsck.close = fsckClose;
1333 	fsck.print = consPrint;
1334 
1335 	ARGBEGIN{
1336 	default:
1337 		return cliError(usage);
1338 	}ARGEND
1339 
1340 	for(i=0; i<argc; i++){
1341 		if(strcmp(argv[i], "pblock") == 0)
1342 			fsck.printblocks = 1;
1343 		else if(strcmp(argv[i], "pdir") == 0)
1344 			fsck.printdirs = 1;
1345 		else if(strcmp(argv[i], "pfile") == 0)
1346 			fsck.printfiles = 1;
1347 		else if(strcmp(argv[i], "bclose") == 0)
1348 			fsck.flags |= DoClose;
1349 		else if(strcmp(argv[i], "clri") == 0)
1350 			fsck.flags |= DoClri;
1351 		else if(strcmp(argv[i], "clre") == 0)
1352 			fsck.flags |= DoClre;
1353 		else if(strcmp(argv[i], "clrp") == 0)
1354 			fsck.flags |= DoClrp;
1355 		else if(strcmp(argv[i], "fix") == 0)
1356 			fsck.flags |= DoClose|DoClri|DoClre|DoClrp;
1357 		else if(strcmp(argv[i], "venti") == 0)
1358 			fsck.useventi = 1;
1359 		else if(strcmp(argv[i], "snapshot") == 0)
1360 			fsck.walksnapshots = 1;
1361 		else{
1362 			consPrint("unknown option '%s'\n", argv[i]);
1363 			return cliError(usage);
1364 		}
1365 	}
1366 
1367 	halting = fsys->fs->halted==0;
1368 	if(halting)
1369 		fsHalt(fsys->fs);
1370 	if(fsys->fs->arch){
1371 		b = superGet(fsys->fs->cache, &super);
1372 		if(b == nil){
1373 			consPrint("could not load super block\n");
1374 			goto Out;
1375 		}
1376 		blockPut(b);
1377 		if(super.current != NilBlock){
1378 			consPrint("cannot check fs while archiver is running; wait for it to finish\n");
1379 			goto Out;
1380 		}
1381 	}
1382 	fsCheck(&fsck);
1383 	consPrint("fsck: %d clri, %d clre, %d clrp, %d bclose\n",
1384 		fsck.nclri, fsck.nclre, fsck.nclrp, fsck.nclose);
1385 Out:
1386 	if(halting)
1387 		fsUnhalt(fsys->fs);
1388 	return 1;
1389 }
1390 
1391 static int
1392 fsysVenti(char* name, int argc, char* argv[])
1393 {
1394 	int r;
1395 	char *host;
1396 	char *usage = "usage: [fsys name] venti [address]";
1397 	Fsys *fsys;
1398 
1399 	ARGBEGIN{
1400 	default:
1401 		return cliError(usage);
1402 	}ARGEND
1403 
1404 	if(argc == 0)
1405 		host = nil;
1406 	else if(argc == 1)
1407 		host = argv[0];
1408 	else
1409 		return cliError(usage);
1410 
1411 	if((fsys = _fsysGet(name)) == nil)
1412 		return 0;
1413 
1414 	vtLock(fsys->lock);
1415 	if(host == nil)
1416 		host = fsys->venti;
1417 	else{
1418 		vtMemFree(fsys->venti);
1419 		if(host[0])
1420 			fsys->venti = vtStrDup(host);
1421 		else{
1422 			host = nil;
1423 			fsys->venti = nil;
1424 		}
1425 	}
1426 
1427 	/* already open: do a redial */
1428 	if(fsys->fs != nil){
1429 		if(fsys->session == nil){
1430 			vtSetError("file system was opened with -V");
1431 			r = 0;
1432 			goto out;
1433 		}
1434 		r = 1;
1435 		if(!vtRedial(fsys->session, host)
1436 		|| !vtConnect(fsys->session, 0))
1437 			r = 0;
1438 		goto out;
1439 	}
1440 
1441 	/* not yet open: try to dial */
1442 	if(fsys->session)
1443 		vtClose(fsys->session);
1444 	r = 1;
1445 	if((fsys->session = vtDial(host, 0)) == nil
1446 	|| !vtConnect(fsys->session, 0))
1447 		r = 0;
1448 out:
1449 	vtUnlock(fsys->lock);
1450 	fsysPut(fsys);
1451 	return r;
1452 }
1453 
1454 static int
1455 fsysOpen(char* name, int argc, char* argv[])
1456 {
1457 	char *p, *host;
1458 	Fsys *fsys;
1459 	long ncache;
1460 	int noauth, noventi, noperm, rflag, wstatallow;
1461 	char *usage = "usage: fsys name open [-APVWr] [-c ncache]";
1462 
1463 	ncache = 1000;
1464 	noauth = noperm = wstatallow = noventi = 0;
1465 	rflag = OReadWrite;
1466 
1467 	ARGBEGIN{
1468 	default:
1469 		return cliError(usage);
1470 	case 'A':
1471 		noauth = 1;
1472 		break;
1473 	case 'P':
1474 		noperm = 1;
1475 		break;
1476 	case 'V':
1477 		noventi = 1;
1478 		break;
1479 	case 'W':
1480 		wstatallow = 1;
1481 		break;
1482 	case 'c':
1483 		p = ARGF();
1484 		if(p == nil)
1485 			return cliError(usage);
1486 		ncache = strtol(argv[0], &p, 0);
1487 		if(ncache <= 0 || p == argv[0] || *p != '\0')
1488 			return cliError(usage);
1489 		break;
1490 	case 'r':
1491 		rflag = OReadOnly;
1492 		break;
1493 	}ARGEND
1494 	if(argc)
1495 		return cliError(usage);
1496 
1497 	if((fsys = _fsysGet(name)) == nil)
1498 		return 0;
1499 
1500 	vtLock(fsys->lock);
1501 	if(fsys->fs != nil){
1502 		vtSetError(EFsysBusy, fsys->name);
1503 		vtUnlock(fsys->lock);
1504 		fsysPut(fsys);
1505 		return 0;
1506 	}
1507 
1508 	if(noventi){
1509 		if(fsys->session){
1510 			vtClose(fsys->session);
1511 			fsys->session = nil;
1512 		}
1513 	}
1514 	else if(fsys->session == nil){
1515 		if(fsys->venti && fsys->venti[0])
1516 			host = fsys->venti;
1517 		else
1518 			host = nil;
1519 		fsys->session = vtDial(host, 1);
1520 		if(!vtConnect(fsys->session, nil) && !noventi)
1521 			fprint(2, "warning: connecting to venti: %R\n");
1522 	}
1523 	if((fsys->fs = fsOpen(fsys->dev, fsys->session, ncache, rflag)) == nil){
1524 		vtSetError("fsOpen: %R");
1525 		vtUnlock(fsys->lock);
1526 		fsysPut(fsys);
1527 		return 0;
1528 	}
1529 	fsys->noauth = noauth;
1530 	fsys->noperm = noperm;
1531 	fsys->wstatallow = wstatallow;
1532 	vtUnlock(fsys->lock);
1533 	fsysPut(fsys);
1534 
1535 	if(strcmp(name, "main") == 0)
1536 		usersFileRead(nil);
1537 
1538 	return 1;
1539 }
1540 
1541 static int
1542 fsysUnconfig(char* name, int argc, char* argv[])
1543 {
1544 	Fsys *fsys, **fp;
1545 	char *usage = "usage: fsys name unconfig";
1546 
1547 	ARGBEGIN{
1548 	default:
1549 		return cliError(usage);
1550 	}ARGEND
1551 	if(argc)
1552 		return cliError(usage);
1553 
1554 	vtLock(sbox.lock);
1555 	fp = &sbox.head;
1556 	for(fsys = *fp; fsys != nil; fsys = fsys->next){
1557 		if(strcmp(fsys->name, name) == 0)
1558 			break;
1559 		fp = &fsys->next;
1560 	}
1561 	if(fsys == nil){
1562 		vtSetError(EFsysNotFound, name);
1563 		vtUnlock(sbox.lock);
1564 		return 0;
1565 	}
1566 	if(fsys->ref != 0 || fsys->fs != nil){
1567 		vtSetError(EFsysBusy, fsys->name);
1568 		vtUnlock(sbox.lock);
1569 		return 0;
1570 	}
1571 	*fp = fsys->next;
1572 	vtUnlock(sbox.lock);
1573 
1574 	if(fsys->session != nil){
1575 		vtClose(fsys->session);
1576 		vtFree(fsys->session);
1577 	}
1578 	if(fsys->venti != nil)
1579 		vtMemFree(fsys->venti);
1580 	if(fsys->dev != nil)
1581 		vtMemFree(fsys->dev);
1582 	if(fsys->name != nil)
1583 		vtMemFree(fsys->name);
1584 	vtMemFree(fsys);
1585 
1586 	return 1;
1587 }
1588 
1589 static int
1590 fsysConfig(char* name, int argc, char* argv[])
1591 {
1592 	Fsys *fsys;
1593 	char *usage = "usage: fsys name config dev";
1594 
1595 	ARGBEGIN{
1596 	default:
1597 		return cliError(usage);
1598 	}ARGEND
1599 	if(argc != 1)
1600 		return cliError(usage);
1601 
1602 	if((fsys = _fsysGet(argv[0])) != nil){
1603 		vtLock(fsys->lock);
1604 		if(fsys->fs != nil){
1605 			vtSetError(EFsysBusy, fsys->name);
1606 			vtUnlock(fsys->lock);
1607 			fsysPut(fsys);
1608 			return 0;
1609 		}
1610 		vtMemFree(fsys->dev);
1611 		fsys->dev = vtStrDup(argv[0]);
1612 		vtUnlock(fsys->lock);
1613 	}
1614 	else if((fsys = fsysAlloc(name, argv[0])) == nil)
1615 		return 0;
1616 
1617 	fsysPut(fsys);
1618 
1619 	return 1;
1620 }
1621 
1622 static struct {
1623 	char*	cmd;
1624 	int	(*f)(Fsys*, int, char**);
1625 	int	(*f1)(char*, int, char**);
1626 } fsyscmd[] = {
1627 	{ "close",	fsysClose, },
1628 	{ "config",	nil, fsysConfig, },
1629 	{ "open",	nil, fsysOpen, },
1630 	{ "unconfig",	nil, fsysUnconfig, },
1631 	{ "venti",	nil, fsysVenti, },
1632 
1633 	{ "bfree",	fsysBfree, },
1634 	{ "block",	fsysBlock, },
1635 	{ "check",	fsysCheck, },
1636 	{ "clre",	fsysClre, },
1637 	{ "clri",	fsysClri, },
1638 	{ "clrp",	fsysClrp, },
1639 	{ "create",	fsysCreate, },
1640 	{ "df",		fsysDf, },
1641 	{ "epoch",	fsysEpoch, },
1642 	{ "halt",	fsysHalt, },
1643 	{ "label",	fsysLabel, },
1644 	{ "remove",	fsysRemove, },
1645 	{ "snap",	fsysSnap, },
1646 	{ "snaptime",	fsysSnapTime, },
1647 	{ "snapclean",	fsysSnapClean, },
1648 	{ "stat",	fsysStat, },
1649 	{ "sync",	fsysSync, },
1650 	{ "unhalt",	fsysUnhalt, },
1651 	{ "wstat",	fsysWstat, },
1652 	{ "vac",	fsysVac, },
1653 
1654 	{ nil,		nil, },
1655 };
1656 
1657 static int
1658 fsysXXX1(Fsys *fsys, int i, int argc, char* argv[])
1659 {
1660 	int r;
1661 
1662 	vtLock(fsys->lock);
1663 	if(fsys->fs == nil){
1664 		vtUnlock(fsys->lock);
1665 		vtSetError(EFsysNotOpen, fsys->name);
1666 		return 0;
1667 	}
1668 
1669 	if(fsys->fs->halted
1670 	&& fsyscmd[i].f != fsysUnhalt && fsyscmd[i].f != fsysCheck){
1671 		vtSetError("file system %s is halted", fsys->name);
1672 		vtUnlock(fsys->lock);
1673 		return 0;
1674 	}
1675 
1676 	r = (*fsyscmd[i].f)(fsys, argc, argv);
1677 	vtUnlock(fsys->lock);
1678 	return r;
1679 }
1680 
1681 static int
1682 fsysXXX(char* name, int argc, char* argv[])
1683 {
1684 	int i, r;
1685 	Fsys *fsys;
1686 
1687 	for(i = 0; fsyscmd[i].cmd != nil; i++){
1688 		if(strcmp(fsyscmd[i].cmd, argv[0]) == 0)
1689 			break;
1690 	}
1691 
1692 	if(fsyscmd[i].cmd == nil){
1693 		vtSetError("unknown command - '%s'", argv[0]);
1694 		return 0;
1695 	}
1696 
1697 	/* some commands want the name... */
1698 	if(fsyscmd[i].f1 != nil){
1699 		if(strcmp(name, FsysAll) == 0){
1700 			vtSetError("cannot use fsys %#q with %#q command", FsysAll, argv[0]);
1701 			return 0;
1702 		}
1703 		return (*fsyscmd[i].f1)(name, argc, argv);
1704 	}
1705 
1706 	/* ... but most commands want the Fsys */
1707 	if(strcmp(name, FsysAll) == 0){
1708 		r = 1;
1709 		vtRLock(sbox.lock);
1710 		for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
1711 			fsys->ref++;
1712 			r = fsysXXX1(fsys, i, argc, argv) && r;
1713 			fsys->ref--;
1714 		}
1715 		vtRUnlock(sbox.lock);
1716 	}else{
1717 		if((fsys = _fsysGet(name)) == nil)
1718 			return 0;
1719 		r = fsysXXX1(fsys, i, argc, argv);
1720 		fsysPut(fsys);
1721 	}
1722 	return r;
1723 }
1724 
1725 static int
1726 cmdFsysXXX(int argc, char* argv[])
1727 {
1728 	char *name;
1729 
1730 	if((name = sbox.curfsys) == nil){
1731 		vtSetError(EFsysNoCurrent, argv[0]);
1732 		return 0;
1733 	}
1734 
1735 	return fsysXXX(name, argc, argv);
1736 }
1737 
1738 static int
1739 cmdFsys(int argc, char* argv[])
1740 {
1741 	Fsys *fsys;
1742 	char *usage = "usage: fsys [name ...]";
1743 
1744 	ARGBEGIN{
1745 	default:
1746 		return cliError(usage);
1747 	}ARGEND
1748 
1749 	if(argc == 0){
1750 		vtRLock(sbox.lock);
1751 		for(fsys = sbox.head; fsys != nil; fsys = fsys->next)
1752 			consPrint("\t%s\n", fsys->name);
1753 		vtRUnlock(sbox.lock);
1754 		return 1;
1755 	}
1756 	if(argc == 1){
1757 		fsys = nil;
1758 		if(strcmp(argv[0], FsysAll) != 0 && (fsys = fsysGet(argv[0])) == nil)
1759 			return 0;
1760 		sbox.curfsys = vtStrDup(argv[0]);
1761 		consPrompt(sbox.curfsys);
1762 		if(fsys)
1763 			fsysPut(fsys);
1764 		return 1;
1765 	}
1766 
1767 	return fsysXXX(argv[0], argc-1, argv+1);
1768 }
1769 
1770 int
1771 fsysInit(void)
1772 {
1773 	int i;
1774 
1775 	fmtinstall('H', encodefmt);
1776 	fmtinstall('V', scoreFmt);
1777 	fmtinstall('R', vtErrFmt);
1778 	fmtinstall('L', labelFmt);
1779 
1780 	sbox.lock = vtLockAlloc();
1781 
1782 	cliAddCmd("fsys", cmdFsys);
1783 	for(i = 0; fsyscmd[i].cmd != nil; i++){
1784 		if(fsyscmd[i].f != nil)
1785 			cliAddCmd(fsyscmd[i].cmd, cmdFsysXXX);
1786 	}
1787 	/* the venti cmd is special: the fs can be either open or closed */
1788 	cliAddCmd("venti", cmdFsysXXX);
1789 	cliAddCmd("printconfig", cmdPrintConfig);
1790 
1791 	return 1;
1792 }
1793