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