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