xref: /plan9-contrib/sys/src/cmd/fossil/9fsys.c (revision a6a9e07217f318acf170f99684a55fba5200524f)
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 typedef 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 } Fsys;
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 EFsysBusy[] = "fsys: '%s' busy";
39 static char EFsysExists[] = "fsys: '%s' already exists";
40 static char EFsysNoCurrent[] = "fsys: no current fsys";
41 static char EFsysNotFound[] = "fsys: '%s' not found";
42 static char EFsysNotOpen[] = "fsys: '%s' not open";
43 
44 static Fsys*
45 _fsysGet(char* name)
46 {
47 	Fsys *fsys;
48 
49 	if(name == nil || name[0] == '\0')
50 		name = "main";
51 
52 	vtRLock(sbox.lock);
53 	for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
54 		if(strcmp(name, fsys->name) == 0){
55 			fsys->ref++;
56 			break;
57 		}
58 	}
59 	if(fsys == nil)
60 		vtSetError(EFsysNotFound, name);
61 	vtRUnlock(sbox.lock);
62 
63 	return fsys;
64 }
65 
66 static int
67 cmdPrintConfig(int argc, char* argv[])
68 {
69 	Fsys *fsys;
70 	char *usage = "usage: printconfig";
71 
72 	ARGBEGIN{
73 	default:
74 		return cliError(usage);
75 	}ARGEND
76 
77 	if(argc)
78 		return cliError(usage);
79 
80 	vtRLock(sbox.lock);
81 	for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
82 		consPrint("\tfsys %s config %s\n", fsys->name, fsys->dev);
83 		if(fsys->venti && fsys->venti[0])
84 			consPrint("\tfsys %s venti %q\n", fsys->name, fsys->venti);
85 	}
86 	vtRUnlock(sbox.lock);
87 	return 1;
88 }
89 
90 Fsys*
91 fsysGet(char* name)
92 {
93 	Fsys *fsys;
94 
95 	if((fsys = _fsysGet(name)) == nil)
96 		return nil;
97 
98 	vtLock(fsys->lock);
99 	if(fsys->fs == nil){
100 		vtSetError(EFsysNotOpen, fsys->name);
101 		vtUnlock(fsys->lock);
102 		fsysPut(fsys);
103 		return nil;
104 	}
105 	vtUnlock(fsys->lock);
106 
107 	return fsys;
108 }
109 
110 char*
111 fsysGetName(Fsys* fsys)
112 {
113 	return fsys->name;
114 }
115 
116 Fsys*
117 fsysIncRef(Fsys* fsys)
118 {
119 	vtLock(sbox.lock);
120 	fsys->ref++;
121 	vtUnlock(sbox.lock);
122 
123 	return fsys;
124 }
125 
126 void
127 fsysPut(Fsys* fsys)
128 {
129 	vtLock(sbox.lock);
130 	assert(fsys->ref > 0);
131 	fsys->ref--;
132 	vtUnlock(sbox.lock);
133 }
134 
135 Fs*
136 fsysGetFs(Fsys* fsys)
137 {
138 	assert(fsys != nil && fsys->fs != nil);
139 
140 	return fsys->fs;
141 }
142 
143 void
144 fsysFsRlock(Fsys* fsys)
145 {
146 	vtRLock(fsys->fs->elk);
147 }
148 
149 void
150 fsysFsRUnlock(Fsys* fsys)
151 {
152 	vtRUnlock(fsys->fs->elk);
153 }
154 
155 int
156 fsysNoAuthCheck(Fsys* fsys)
157 {
158 	return fsys->noauth;
159 }
160 
161 int
162 fsysNoPermCheck(Fsys* fsys)
163 {
164 	return fsys->noperm;
165 }
166 
167 int
168 fsysWstatAllow(Fsys* fsys)
169 {
170 	return fsys->wstatallow;
171 }
172 
173 static char modechars[] = "YUGalLdHSATs";
174 static ulong modebits[] = {
175 	ModeSticky,
176 	ModeSetUid,
177 	ModeSetGid,
178 	ModeAppend,
179 	ModeExclusive,
180 	ModeLink,
181 	ModeDir,
182 	ModeHidden,
183 	ModeSystem,
184 	ModeArchive,
185 	ModeTemporary,
186 	ModeSnapshot,
187 	0
188 };
189 
190 char*
191 fsysModeString(ulong mode, char *buf)
192 {
193 	int i;
194 	char *p;
195 
196 	p = buf;
197 	for(i=0; modebits[i]; i++)
198 		if(mode & modebits[i])
199 			*p++ = modechars[i];
200 	sprint(p, "%luo", mode&0777);
201 	return buf;
202 }
203 
204 int
205 fsysParseMode(char* s, ulong* mode)
206 {
207 	ulong x, y;
208 	char *p;
209 
210 	x = 0;
211 	for(; *s < '0' || *s > '9'; s++){
212 		if(*s == 0)
213 			return 0;
214 		p = strchr(modechars, *s);
215 		if(p == nil)
216 			return 0;
217 		x |= modebits[p-modechars];
218 	}
219 	y = strtoul(s, &p, 8);
220 	if(*p != '\0' || y > 0777)
221 		return 0;
222 	*mode = x|y;
223 	return 1;
224 }
225 
226 File*
227 fsysGetRoot(Fsys* fsys, char* name)
228 {
229 	File *root, *sub;
230 
231 	assert(fsys != nil && fsys->fs != nil);
232 
233 	root = fsGetRoot(fsys->fs);
234 	if(name == nil || strcmp(name, "") == 0)
235 		return root;
236 
237 	sub = fileWalk(root, name);
238 	fileDecRef(root);
239 
240 	return sub;
241 }
242 
243 static Fsys*
244 fsysAlloc(char* name, char* dev)
245 {
246 	Fsys *fsys;
247 
248 	vtLock(sbox.lock);
249 	for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
250 		if(strcmp(fsys->name, name) != 0)
251 			continue;
252 		vtSetError(EFsysExists, name);
253 		vtUnlock(sbox.lock);
254 		return nil;
255 	}
256 
257 	fsys = vtMemAllocZ(sizeof(Fsys));
258 	fsys->lock = vtLockAlloc();
259 	fsys->name = vtStrDup(name);
260 	fsys->dev = vtStrDup(dev);
261 
262 	fsys->ref = 1;
263 
264 	if(sbox.tail != nil)
265 		sbox.tail->next = fsys;
266 	else
267 		sbox.head = fsys;
268 	sbox.tail = fsys;
269 	vtUnlock(sbox.lock);
270 
271 	return fsys;
272 }
273 
274 static int
275 fsysClose(Fsys* fsys, int argc, char* argv[])
276 {
277 	char *usage = "usage: [fsys name] close";
278 
279 	ARGBEGIN{
280 	default:
281 		return cliError(usage);
282 	}ARGEND
283 	if(argc)
284 		return cliError(usage);
285 
286 	return cliError("close isn't working yet; sync and then kill fossil");
287 
288 	/*
289 	 * Oooh. This could be hard. What if fsys->ref != 1?
290 	 * Also, fsClose() either does the job or panics, can we
291 	 * gracefully detect it's still busy?
292 	 *
293 	 * More thought and care needed here.
294 	 */
295 	fsClose(fsys->fs);
296 	fsys->fs = nil;
297 	vtClose(fsys->session);
298 	fsys->session = nil;
299 
300 	if(sbox.curfsys != nil && strcmp(fsys->name, sbox.curfsys) == 0){
301 		sbox.curfsys = nil;
302 		consPrompt(nil);
303 	}
304 
305 	return 1;
306 }
307 
308 static int
309 fsysVac(Fsys* fsys, int argc, char* argv[])
310 {
311 	uchar score[VtScoreSize];
312 	char *usage = "usage: [fsys name] vac path";
313 
314 	ARGBEGIN{
315 	default:
316 		return cliError(usage);
317 	}ARGEND
318 	if(argc != 1)
319 		return cliError(usage);
320 
321 	if(!fsVac(fsys->fs, argv[0], score))
322 		return 0;
323 
324 	consPrint("vac:%V\n", score);
325 	return 1;
326 }
327 
328 static int
329 fsysSnap(Fsys* fsys, int argc, char* argv[])
330 {
331 	int doarchive;
332 	char *usage = "usage: [fsys name] snap [-a]";
333 
334 	doarchive = 0;
335 	ARGBEGIN{
336 	default:
337 		return cliError(usage);
338 	case 'a':
339 		doarchive = 1;
340 		break;
341 	}ARGEND
342 	if(argc)
343 		return cliError(usage);
344 
345 	if(!fsSnapshot(fsys->fs, doarchive))
346 		return 0;
347 
348 	return 1;
349 }
350 
351 static int
352 fsysSnapTime(Fsys* fsys, int argc, char* argv[])
353 {
354 	char buf[40], *x;
355 	int hh, mm;
356 	u32int arch, snap;
357 	char *usage = "usage: [fsys name] snaptime [-a hhmm] [-s minutes]";
358 
359 	snapGetTimes(fsys->fs->snap, &arch, &snap);
360 	ARGBEGIN{
361 	case 'a':
362 		x = ARGF();
363 		if(x == nil)
364 			return cliError(usage);
365 		if(strcmp(x, "none") == 0){
366 			arch = ~(u32int)0;
367 			break;
368 		}
369 		if(strlen(x) != 4 || strspn(x, "0123456789") != 4)
370 			return cliError(usage);
371 		hh = (x[0]-'0')*10 + x[1]-'0';
372 		mm = (x[2]-'0')*10 + x[3]-'0';
373 		if(hh >= 24 || mm >= 60)
374 			return cliError(usage);
375 		arch = hh*60+mm;
376 		break;
377 	case 's':
378 		x = ARGF();
379 		if(x == nil)
380 			return cliError(usage);
381 		if(strcmp(x, "none") == 0){
382 			snap = ~(u32int)0;
383 			break;
384 		}
385 		snap = atoi(x);
386 		break;
387 	default:
388 		return cliError(usage);
389 	}ARGEND
390 	if(argc > 0)
391 		return cliError(usage);
392 
393 	snapSetTimes(fsys->fs->snap, arch, snap);
394 	snapGetTimes(fsys->fs->snap, &arch, &snap);
395 	if(arch != ~(u32int)0)
396 		sprint(buf, "-a %02d%02d", arch/60, arch%60);
397 	else
398 		sprint(buf, "-a none");
399 	if(snap != ~(u32int)0)
400 		sprint(buf+strlen(buf), " -s %d", snap);
401 	else
402 		sprint(buf+strlen(buf), " -s none");
403 	consPrint("\tsnaptime %s\n", buf);
404 	return 1;
405 }
406 
407 static int
408 fsysSync(Fsys* fsys, int argc, char* argv[])
409 {
410 	char *usage = "usage: [fsys name] sync";
411 
412 	ARGBEGIN{
413 	default:
414 		return cliError(usage);
415 	}ARGEND
416 	if(argc > 0)
417 		return cliError(usage);
418 
419 	fsSync(fsys->fs);
420 
421 	return 1;
422 }
423 
424 static int
425 fsysRemove(Fsys* fsys, int argc, char* argv[])
426 {
427 	File *file;
428 	char *usage = "usage: [fsys name] remove path ...";
429 
430 	ARGBEGIN{
431 	default:
432 		return cliError(usage);
433 	}ARGEND
434 	if(argc == 0)
435 		return cliError(usage);
436 
437 	vtRLock(fsys->fs->elk);
438 	while(argc > 0){
439 		if((file = fileOpen(fsys->fs, argv[0])) == nil)
440 			consPrint("%s: %R\n", argv[0]);
441 		else{
442 			if(!fileRemove(file, uidadm))
443 				consPrint("%s: %R\n", argv[0]);
444 			fileDecRef(file);
445 		}
446 		argc--;
447 		argv++;
448 	}
449 	vtRUnlock(fsys->fs->elk);
450 
451 	return 1;
452 }
453 
454 static int
455 fsysClri(Fsys* fsys, int argc, char* argv[])
456 {
457 	char *usage = "usage: [fsys name] clri path ...";
458 
459 	ARGBEGIN{
460 	default:
461 		return cliError(usage);
462 	}ARGEND
463 	if(argc == 0)
464 		return cliError(usage);
465 
466 	vtRLock(fsys->fs->elk);
467 	while(argc > 0){
468 		if(!fileClri(fsys->fs, argv[0], uidadm))
469 			consPrint("clri %s: %R\n", argv[0]);
470 		argc--;
471 		argv++;
472 	}
473 	vtRUnlock(fsys->fs->elk);
474 
475 	return 1;
476 }
477 
478 /*
479  * Inspect and edit the labels for blocks on disk.
480  */
481 static int
482 fsysLabel(Fsys* fsys, int argc, char* argv[])
483 {
484 	Fs *fs;
485 	Label l;
486 	int n, r;
487 	u32int addr;
488 	Block *b, *bb;
489 	char *usage = "usage: [fsys name] label addr [type state epoch epochClose tag]";
490 
491 	ARGBEGIN{
492 	default:
493 		return cliError(usage);
494 	}ARGEND
495 	if(argc != 1 && argc != 6)
496 		return cliError(usage);
497 
498 	r = 0;
499 	vtRLock(fsys->fs->elk);
500 
501 	fs = fsys->fs;
502 	addr = strtoul(argv[0], 0, 0);
503 	b = cacheLocal(fs->cache, PartData, addr, OReadOnly);
504 	if(b == nil)
505 		goto Out0;
506 
507 	l = b->l;
508 	consPrint("%slabel %#ux %ud %ud %ud %ud %#x\n",
509 		argc==6 ? "old: " : "", addr, l.type, l.state,
510 		l.epoch, l.epochClose, l.tag);
511 
512 	if(argc == 6){
513 		if(strcmp(argv[1], "-") != 0)
514 			l.type = atoi(argv[1]);
515 		if(strcmp(argv[2], "-") != 0)
516 			l.state = atoi(argv[2]);
517 		if(strcmp(argv[3], "-") != 0)
518 			l.epoch = strtoul(argv[3], 0, 0);
519 		if(strcmp(argv[4], "-") != 0)
520 			l.epochClose = strtoul(argv[4], 0, 0);
521 		if(strcmp(argv[5], "-") != 0)
522 			l.tag = strtoul(argv[5], 0, 0);
523 
524 		consPrint("new: label %#ux %ud %ud %ud %ud %#x\n",
525 			addr, l.type, l.state, l.epoch, l.epochClose, l.tag);
526 		bb = _blockSetLabel(b, &l);
527 		if(bb == nil)
528 			goto Out1;
529 		n = 0;
530 		for(;;){
531 			if(blockWrite(bb)){
532 				while(bb->iostate != BioClean){
533 					assert(bb->iostate == BioWriting);
534 					vtSleep(bb->ioready);
535 				}
536 				break;
537 			}
538 			consPrint("blockWrite: %R\n");
539 			if(n++ >= 5){
540 				consPrint("giving up\n");
541 				break;
542 			}
543 			sleep(5*1000);
544 		}
545 		blockPut(bb);
546 	}
547 	r = 1;
548 Out1:
549 	blockPut(b);
550 Out0:
551 	vtRUnlock(fs->elk);
552 
553 	return r;
554 }
555 
556 /*
557  * Inspect and edit the blocks on disk.
558  */
559 static int
560 fsysBlock(Fsys* fsys, int argc, char* argv[])
561 {
562 	Fs *fs;
563 	char *s;
564 	Block *b;
565 	uchar *buf;
566 	u32int addr;
567 	int c, count, i, offset;
568 	char *usage = "usage: [fsys name] block addr offset [count [data]]";
569 
570 	ARGBEGIN{
571 	default:
572 		return cliError(usage);
573 	}ARGEND
574 	if(argc < 2 || argc > 4)
575 		return cliError(usage);
576 
577 	fs = fsys->fs;
578 	addr = strtoul(argv[0], 0, 0);
579 	offset = strtoul(argv[1], 0, 0);
580 	if(offset < 0 || offset >= fs->blockSize){
581 		vtSetError("bad offset");
582 		return 0;
583 	}
584 	if(argc > 2)
585 		count = strtoul(argv[2], 0, 0);
586 	else
587 		count = 100000000;
588 	if(offset+count > fs->blockSize)
589 		count = fs->blockSize - count;
590 
591 	vtRLock(fs->elk);
592 
593 	b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly);
594 	if(b == nil){
595 		vtSetError("cacheLocal %#ux: %R", addr);
596 		vtRUnlock(fs->elk);
597 		return 0;
598 	}
599 
600 	consPrint("\t%sblock %#ux %ud %ud %.*H\n",
601 		argc==4 ? "old: " : "", addr, offset, count, count, b->data+offset);
602 
603 	if(argc == 4){
604 		s = argv[3];
605 		if(strlen(s) != 2*count){
606 			vtSetError("bad data count");
607 			goto Out;
608 		}
609 		buf = vtMemAllocZ(count);
610 		for(i = 0; i < count*2; i++){
611 			if(s[i] >= '0' && s[i] <= '9')
612 				c = s[i] - '0';
613 			else if(s[i] >= 'a' && s[i] <= 'f')
614 				c = s[i] - 'a' + 10;
615 			else if(s[i] >= 'A' && s[i] <= 'F')
616 				c = s[i] - 'A' + 10;
617 			else{
618 				vtSetError("bad hex");
619 				vtMemFree(buf);
620 				goto Out;
621 			}
622 			if((i & 1) == 0)
623 				c <<= 4;
624 			buf[i>>1] |= c;
625 		}
626 		memmove(b->data+offset, buf, count);
627 		consPrint("\tnew: block %#ux %ud %ud %.*H\n",
628 			addr, offset, count, count, b->data+offset);
629 		blockDirty(b);
630 	}
631 
632 Out:
633 	blockPut(b);
634 	vtRUnlock(fs->elk);
635 
636 	return 1;
637 }
638 
639 /*
640  * Free a disk block.
641  */
642 static int
643 fsysBfree(Fsys* fsys, int argc, char* argv[])
644 {
645 	Fs *fs;
646 	Label l;
647 	char *p;
648 	Block *b;
649 	u32int addr;
650 	char *usage = "usage: [fsys name] bfree addr ...";
651 
652 	ARGBEGIN{
653 	default:
654 		return cliError(usage);
655 	}ARGEND
656 	if(argc == 0)
657 		return cliError(usage);
658 
659 	fs = fsys->fs;
660 	vtRLock(fs->elk);
661 	while(argc > 0){
662 		addr = strtoul(argv[0], &p, 0);
663 		if(*p != '\0'){
664 			consPrint("bad address - '%s'\n", addr);
665 			/* syntax error; let's stop */
666 			vtRUnlock(fs->elk);
667 			return 0;
668 		}
669 		b = cacheLocal(fs->cache, PartData, addr, OReadOnly);
670 		if(b == nil){
671 			consPrint("loading %#ux: %R\n", addr);
672 			continue;
673 		}
674 		l = b->l;
675 		consPrint("label %#ux %ud %ud %ud %ud %#x\n",
676 			addr, l.type, l.state, l.epoch, l.epochClose, l.tag);
677 		l.state = BsFree;
678 		l.type = BtMax;
679 		l.tag = 0;
680 		l.epoch = 0;
681 		l.epochClose = 0;
682 		if(!blockSetLabel(b, &l))
683 			consPrint("freeing %#ux: %R\n", addr);
684 		blockPut(b);
685 		argc--;
686 		argv++;
687 	}
688 	vtRUnlock(fs->elk);
689 
690 	return 1;
691 }
692 
693 /*
694  * Zero an entry or a pointer.
695  */
696 static int
697 fsysClrep(Fsys* fsys, int argc, char* argv[], int ch)
698 {
699 	Fs *fs;
700 	Entry e;
701 	Block *b;
702 	u32int addr;
703 	int i, max, offset, sz;
704 	uchar zero[VtEntrySize];
705 	char *usage = "usage: [fsys name] clr%c addr offset ...";
706 
707 	ARGBEGIN{
708 	default:
709 		return cliError(usage, ch);
710 	}ARGEND
711 	if(argc < 2)
712 		return cliError(usage, ch);
713 
714 	fs = fsys->fs;
715 	vtRLock(fsys->fs->elk);
716 
717 	addr = strtoul(argv[0], 0, 0);
718 	b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly);
719 	if(b == nil){
720 		vtSetError("cacheLocal %#ux: %R", addr);
721 	Err:
722 		vtRUnlock(fsys->fs->elk);
723 		return 0;
724 	}
725 
726 	switch(ch){
727 	default:
728 		vtSetError("clrep");
729 		goto Err;
730 	case 'e':
731 		if(b->l.type != BtDir){
732 			vtSetError("wrong block type");
733 			goto Err;
734 		}
735 		sz = VtEntrySize;
736 		memset(&e, 0, sizeof e);
737 		entryPack(&e, zero, 0);
738 		break;
739 	case 'p':
740 		if(b->l.type == BtDir || b->l.type == BtData){
741 			vtSetError("wrong block type");
742 			goto Err;
743 		}
744 		sz = VtScoreSize;
745 		memmove(zero, vtZeroScore, VtScoreSize);
746 		break;
747 	}
748 	max = fs->blockSize/sz;
749 
750 	for(i = 1; i < argc; i++){
751 		offset = atoi(argv[i]);
752 		if(offset >= max){
753 			consPrint("\toffset %d too large (>= %d)\n", i, max);
754 			continue;
755 		}
756 		consPrint("\tblock %#ux %d %d %.*H\n", addr, offset*sz, sz, sz, b->data+offset*sz);
757 		memmove(b->data+offset*sz, zero, sz);
758 	}
759 	blockDirty(b);
760 	blockPut(b);
761 	vtRUnlock(fsys->fs->elk);
762 
763 	return 1;
764 }
765 
766 static int
767 fsysClre(Fsys* fsys, int argc, char* argv[])
768 {
769 	return fsysClrep(fsys, argc, argv, 'e');
770 }
771 
772 static int
773 fsysClrp(Fsys* fsys, int argc, char* argv[])
774 {
775 	return fsysClrep(fsys, argc, argv, 'p');
776 }
777 
778 static int
779 fsysEsearch1(File* f, char* s, u32int elo)
780 {
781 	int n, r;
782 	DirEntry de;
783 	DirEntryEnum *dee;
784 	File *ff;
785 	Entry e, ee;
786 	char *t;
787 
788 	dee = deeOpen(f);
789 	if(dee == nil)
790 		return 0;
791 
792 	n = 0;
793 	for(;;){
794 		r = deeRead(dee, &de);
795 		if(r < 0){
796 			consPrint("\tdeeRead %s/%s: %R\n", s, de.elem);
797 			break;
798 		}
799 		if(r == 0)
800 			break;
801 		if(de.mode & ModeSnapshot){
802 			if((ff = fileWalk(f, de.elem)) == nil)
803 				consPrint("\tcannot walk %s/%s: %R\n", s, de.elem);
804 			else{
805 				if(!fileGetSources(ff, &e, &ee, 0))
806 					consPrint("\tcannot get sources for %s/%s: %R\n", s, de.elem);
807 				else if(e.snap != 0 && e.snap < elo){
808 					consPrint("\t%ud\tclri %s/%s\n", e.snap, s, de.elem);
809 					n++;
810 				}
811 				fileDecRef(ff);
812 			}
813 		}
814 		else if(de.mode & ModeDir){
815 			if((ff = fileWalk(f, de.elem)) == nil)
816 				consPrint("\tcannot walk %s/%s: %R\n", s, de.elem);
817 			else{
818 				t = smprint("%s/%s", s, de.elem);
819 				n += fsysEsearch1(ff, t, elo);
820 				vtMemFree(t);
821 				fileDecRef(ff);
822 			}
823 		}
824 		deCleanup(&de);
825 		if(r < 0)
826 			break;
827 	}
828 	deeClose(dee);
829 
830 	return n;
831 }
832 
833 static int
834 fsysEsearch(Fs* fs, char* path, u32int elo)
835 {
836 	int n;
837 	File *f;
838 	DirEntry de;
839 
840 	f = fileOpen(fs, path);
841 	if(f == nil)
842 		return 0;
843 	if(!fileGetDir(f, &de)){
844 		consPrint("\tfileGetDir %s failed: %R\n", path);
845 		fileDecRef(f);
846 		return 0;
847 	}
848 	if((de.mode & ModeDir) == 0){
849 		fileDecRef(f);
850 		deCleanup(&de);
851 		return 0;
852 	}
853 	deCleanup(&de);
854 	n = fsysEsearch1(f, path, elo);
855 	fileDecRef(f);
856 	return n;
857 }
858 
859 static int
860 fsysEpoch(Fsys* fsys, int argc, char* argv[])
861 {
862 	Fs *fs;
863 	int force, n;
864 	u32int low, old;
865 	char *usage = "usage: [fsys name] epoch [[-y] low]";
866 
867 	force = 0;
868 	ARGBEGIN{
869 	case 'y':
870 		force = 1;
871 		break;
872 	default:
873 		return cliError(usage);
874 	}ARGEND
875 	if(argc > 1)
876 		return cliError(usage);
877 	if(argc > 0)
878 		low = strtoul(argv[0], 0, 0);
879 	else
880 		low = ~(u32int)0;
881 
882 	fs = fsys->fs;
883 
884 	vtRLock(fs->elk);
885 	consPrint("\tlow %ud hi %ud\n", fs->elo, fs->ehi);
886 	n = fsysEsearch(fsys->fs, "/archive", low);
887 	n += fsysEsearch(fsys->fs, "/snapshot", low);
888 	consPrint("\t%d snapshot%s found with epoch < %ud\n", n, n==1 ? "" : "s", low);
889 	vtRUnlock(fs->elk);
890 
891 	/*
892 	 * There's a small race here -- a new snapshot with epoch < low might
893 	 * get introduced now that we unlocked fs->elk.  Low has to
894 	 * be <= fs->ehi.  Of course, in order for this to happen low has
895 	 * to be equal to the current fs->ehi _and_ a snapshot has to
896 	 * run right now.  This is a small enough window that I don't care.
897 	 */
898 	if(n != 0 && !force){
899 		consPrint("\tnot setting low epoch\n");
900 		return 1;
901 	}
902 	old = fs->elo;
903 	if(!fsEpochLow(fs, low))
904 		consPrint("\tfsEpochLow: %R\n");
905 	else{
906 		consPrint("\told: epoch%s %ud\n", force ? " -y" : "", old);
907 		consPrint("\tnew: epoch%s %ud\n", force ? " -y" : "", fs->elo);
908 		if(fs->elo < low)
909 			consPrint("\twarning: new low epoch < old low epoch\n");
910 	}
911 
912 	return 1;
913 }
914 
915 static int
916 fsysCreate(Fsys* fsys, int argc, char* argv[])
917 {
918 	int r;
919 	ulong mode;
920 	char *elem, *p, *path;
921 	char *usage = "usage: [fsys name] create path uid gid perm";
922 	DirEntry de;
923 	File *file, *parent;
924 
925 	ARGBEGIN{
926 	default:
927 		return cliError(usage);
928 	}ARGEND
929 	if(argc != 4)
930 		return cliError(usage);
931 
932 	if(!fsysParseMode(argv[3], &mode))
933 		return cliError(usage);
934 	if(mode&ModeSnapshot)
935 		return cliError("create - cannot create with snapshot bit set");
936 
937 	if(strcmp(argv[1], uidnoworld) == 0)
938 		return cliError("permission denied");
939 
940 	vtRLock(fsys->fs->elk);
941 	path = vtStrDup(argv[0]);
942 	if((p = strrchr(path, '/')) != nil){
943 		*p++ = '\0';
944 		elem = p;
945 		p = path;
946 		if(*p == '\0')
947 			p = "/";
948 	}
949 	else{
950 		p = "/";
951 		elem = path;
952 	}
953 
954 	r = 0;
955 	if((parent = fileOpen(fsys->fs, p)) == nil)
956 		goto out;
957 
958 	file = fileCreate(parent, elem, mode, argv[1]);
959 	fileDecRef(parent);
960 	if(file == nil){
961 		vtSetError("create %s/%s: %R", p, elem);
962 		goto out;
963 	}
964 
965 	if(!fileGetDir(file, &de)){
966 		vtSetError("stat failed after create: %R");
967 		goto out1;
968 	}
969 
970 	if(strcmp(de.gid, argv[2]) != 0){
971 		vtMemFree(de.gid);
972 		de.gid = vtStrDup(argv[2]);
973 		if(!fileSetDir(file, &de, argv[1])){
974 			vtSetError("wstat failed after create: %R");
975 			goto out2;
976 		}
977 	}
978 	r = 1;
979 
980 out2:
981 	deCleanup(&de);
982 out1:
983 	fileDecRef(file);
984 out:
985 	vtMemFree(path);
986 	vtRUnlock(fsys->fs->elk);
987 
988 	return r;
989 }
990 
991 static void
992 fsysPrintStat(char *prefix, char *file, DirEntry *de)
993 {
994 	char buf[64];
995 
996 	if(prefix == nil)
997 		prefix = "";
998 	consPrint("%sstat %q %q %q %q %s %llud\n", prefix,
999 		file, de->elem, de->uid, de->gid, fsysModeString(de->mode, buf), de->size);
1000 }
1001 
1002 static int
1003 fsysStat(Fsys* fsys, int argc, char* argv[])
1004 {
1005 	int i;
1006 	File *f;
1007 	DirEntry de;
1008 	char *usage = "usage: [fsys name] stat files...";
1009 
1010 	ARGBEGIN{
1011 	default:
1012 		return cliError(usage);
1013 	}ARGEND
1014 
1015 	if(argc == 0)
1016 		return cliError(usage);
1017 
1018 	vtRLock(fsys->fs->elk);
1019 	for(i=0; i<argc; i++){
1020 		if((f = fileOpen(fsys->fs, argv[i])) == nil){
1021 			consPrint("%s: %R\n");
1022 			continue;
1023 		}
1024 		if(!fileGetDir(f, &de)){
1025 			consPrint("%s: %R\n");
1026 			fileDecRef(f);
1027 			continue;
1028 		}
1029 		fsysPrintStat("\t", argv[i], &de);
1030 		deCleanup(&de);
1031 		fileDecRef(f);
1032 	}
1033 	vtRUnlock(fsys->fs->elk);
1034 	return 1;
1035 }
1036 
1037 static int
1038 fsysWstat(Fsys *fsys, int argc, char* argv[])
1039 {
1040 	File *f;
1041 	char *p;
1042 	DirEntry de;
1043 	char *usage = "usage: [fsys name] wstat file elem uid gid mode length\n"
1044 		"\tuse - for any field to mean don't change";
1045 
1046 	ARGBEGIN{
1047 	default:
1048 		return cliError(usage);
1049 	}ARGEND
1050 
1051 	if(argc != 6)
1052 		return cliError(usage);
1053 
1054 	vtRLock(fsys->fs->elk);
1055 	if((f = fileOpen(fsys->fs, argv[0])) == nil){
1056 		vtSetError("console wstat - walk - %R");
1057 		vtRUnlock(fsys->fs->elk);
1058 		return 0;
1059 	}
1060 	if(!fileGetDir(f, &de)){
1061 		vtSetError("console wstat - stat - %R");
1062 		fileDecRef(f);
1063 		vtRUnlock(fsys->fs->elk);
1064 		return 0;
1065 	}
1066 	fsysPrintStat("\told: w", argv[0], &de);
1067 
1068 	if(strcmp(argv[1], "-") != 0){
1069 		if(!validFileName(argv[1])){
1070 			vtSetError("console wstat - bad elem");
1071 			goto error;
1072 		}
1073 		vtMemFree(de.elem);
1074 		de.elem = vtStrDup(argv[1]);
1075 	}
1076 	if(strcmp(argv[2], "-") != 0){
1077 		if(!validUserName(argv[2])){
1078 			vtSetError("console wstat - bad uid");
1079 			goto error;
1080 		}
1081 		vtMemFree(de.uid);
1082 		de.uid = vtStrDup(argv[2]);
1083 	}
1084 	if(strcmp(argv[3], "-") != 0){
1085 		if(!validUserName(argv[3])){
1086 			vtSetError("console wstat - bad gid");
1087 			goto error;
1088 		}
1089 		vtMemFree(de.gid);
1090 		de.gid = vtStrDup(argv[3]);
1091 	}
1092 	if(strcmp(argv[4], "-") != 0){
1093 		if(!fsysParseMode(argv[4], &de.mode)){
1094 			vtSetError("console wstat - bad mode");
1095 			goto error;
1096 		}
1097 	}
1098 	if(strcmp(argv[5], "-") != 0){
1099 		de.size = strtoull(argv[5], &p, 0);
1100 		if(argv[5][0] == '\0' || *p != '\0' || de.size < 0){
1101 			vtSetError("console wstat - bad length");
1102 			goto error;
1103 		}
1104 	}
1105 
1106 	if(!fileSetDir(f, &de, uidadm)){
1107 		vtSetError("console wstat - %R");
1108 		goto error;
1109 	}
1110 	deCleanup(&de);
1111 
1112 	if(!fileGetDir(f, &de)){
1113 		vtSetError("console wstat - stat2 - %R");
1114 		goto error;
1115 	}
1116 	fsysPrintStat("\tnew: w", argv[0], &de);
1117 	deCleanup(&de);
1118 	fileDecRef(f);
1119 	vtRUnlock(fsys->fs->elk);
1120 
1121 	return 1;
1122 
1123 error:
1124 	deCleanup(&de);	/* okay to do this twice */
1125 	fileDecRef(f);
1126 	vtRUnlock(fsys->fs->elk);
1127 	return 0;
1128 }
1129 
1130 static int
1131 fsysVenti(char* name, int argc, char* argv[])
1132 {
1133 	int r;
1134 	char *host;
1135 	char *usage = "usage: [fsys name] venti [address]";
1136 	Fsys *fsys;
1137 
1138 	ARGBEGIN{
1139 	default:
1140 		return cliError(usage);
1141 	}ARGEND
1142 
1143 	if(argc == 0)
1144 		host = nil;
1145 	else if(argc == 1)
1146 		host = argv[0];
1147 	else
1148 		return cliError(usage);
1149 
1150 	if((fsys = _fsysGet(name)) == nil)
1151 		return 0;
1152 
1153 	vtLock(fsys->lock);
1154 	if(host == nil)
1155 		host = fsys->venti;
1156 	else{
1157 		vtMemFree(fsys->venti);
1158 		if(host[0])
1159 			fsys->venti = vtStrDup(host);
1160 		else{
1161 			host = nil;
1162 			fsys->venti = nil;
1163 		}
1164 	}
1165 
1166 	/* already open: do a redial */
1167 	if(fsys->fs != nil){
1168 		r = 1;
1169 		if(!vtRedial(fsys->session, host)
1170 		|| !vtConnect(fsys->session, 0))
1171 			r = 0;
1172 		vtUnlock(fsys->lock);
1173 		fsysPut(fsys);
1174 		return r;
1175 	}
1176 
1177 	/* not yet open: try to dial */
1178 	if(fsys->session)
1179 		vtClose(fsys->session);
1180 	r = 1;
1181 	if((fsys->session = vtDial(host, 0)) == nil
1182 	|| !vtConnect(fsys->session, 0))
1183 		r = 0;
1184 	vtUnlock(fsys->lock);
1185 	fsysPut(fsys);
1186 	return r;
1187 }
1188 
1189 static int
1190 fsysOpen(char* name, int argc, char* argv[])
1191 {
1192 	char *p, *host;
1193 	Fsys *fsys;
1194 	long ncache;
1195 	int noauth, noperm, rflag, wstatallow;
1196 	char *usage = "usage: fsys name open [-APWr] [-c ncache]";
1197 
1198 	ncache = 1000;
1199 	noauth = noperm = wstatallow = 0;
1200 	rflag = OReadWrite;
1201 
1202 	ARGBEGIN{
1203 	default:
1204 		return cliError(usage);
1205 	case 'A':
1206 		noauth = 1;
1207 		break;
1208 	case 'P':
1209 		noperm = 1;
1210 		break;
1211 	case 'W':
1212 		wstatallow = 1;
1213 		break;
1214 	case 'c':
1215 		p = ARGF();
1216 		if(p == nil)
1217 			return cliError(usage);
1218 		ncache = strtol(argv[0], &p, 0);
1219 		if(ncache <= 0 || p == argv[0] || *p != '\0')
1220 			return cliError(usage);
1221 		break;
1222 	case 'r':
1223 		rflag = OReadOnly;
1224 		break;
1225 	}ARGEND
1226 	if(argc)
1227 		return cliError(usage);
1228 
1229 	if((fsys = _fsysGet(name)) == nil)
1230 		return 0;
1231 
1232 	vtLock(fsys->lock);
1233 	if(fsys->fs != nil){
1234 		vtSetError(EFsysBusy, fsys->name);
1235 		vtUnlock(fsys->lock);
1236 		fsysPut(fsys);
1237 		return 0;
1238 	}
1239 
1240 	if(fsys->session == nil){
1241 		if(fsys->venti && fsys->venti[0])
1242 			host = fsys->venti;
1243 		else
1244 			host = nil;
1245 		fsys->session = vtDial(host, 1);
1246 		if(!vtConnect(fsys->session, nil))
1247 			fprint(2, "warning: connecting to venti: %R\n");
1248 	}
1249 	if((fsys->fs = fsOpen(fsys->dev, fsys->session, ncache, rflag)) == nil){
1250 		vtUnlock(fsys->lock);
1251 		fsysPut(fsys);
1252 		return 0;
1253 	}
1254 	fsys->noauth = noauth;
1255 	fsys->noperm = noperm;
1256 	fsys->wstatallow = wstatallow;
1257 	vtUnlock(fsys->lock);
1258 
1259 	fsysPut(fsys);
1260 
1261 	return 1;
1262 }
1263 
1264 static int
1265 fsysUnconfig(char* name, int argc, char* argv[])
1266 {
1267 	Fsys *fsys, **fp;
1268 	char *usage = "usage: fsys name unconfig";
1269 
1270 	ARGBEGIN{
1271 	default:
1272 		return cliError(usage);
1273 	}ARGEND
1274 	if(argc)
1275 		return cliError(usage);
1276 
1277 	vtLock(sbox.lock);
1278 	fp = &sbox.head;
1279 	for(fsys = *fp; fsys != nil; fsys = fsys->next){
1280 		if(strcmp(fsys->name, name) == 0)
1281 			break;
1282 		fp = &fsys->next;
1283 	}
1284 	if(fsys == nil){
1285 		vtSetError(EFsysNotFound, name);
1286 		vtUnlock(sbox.lock);
1287 		return 0;
1288 	}
1289 	if(fsys->ref != 0 || fsys->fs != nil){
1290 		vtSetError(EFsysBusy, fsys->name);
1291 		vtUnlock(sbox.lock);
1292 		return 0;
1293 	}
1294 	*fp = fsys->next;
1295 	vtUnlock(sbox.lock);
1296 
1297 	if(fsys->session != nil){
1298 		vtClose(fsys->session);
1299 		vtFree(fsys->session);
1300 	}
1301 	if(fsys->venti != nil)
1302 		vtMemFree(fsys->venti);
1303 	if(fsys->dev != nil)
1304 		vtMemFree(fsys->dev);
1305 	if(fsys->name != nil)
1306 		vtMemFree(fsys->name);
1307 	vtMemFree(fsys);
1308 
1309 	return 1;
1310 }
1311 
1312 static int
1313 fsysConfig(char* name, int argc, char* argv[])
1314 {
1315 	Fsys *fsys;
1316 	char *usage = "usage: fsys name config dev";
1317 
1318 	ARGBEGIN{
1319 	default:
1320 		return cliError(usage);
1321 	}ARGEND
1322 	if(argc != 1)
1323 		return cliError(usage);
1324 
1325 	if((fsys = _fsysGet(argv[0])) != nil){
1326 		vtLock(fsys->lock);
1327 		if(fsys->fs != nil){
1328 			vtSetError(EFsysBusy, fsys->name);
1329 			vtUnlock(fsys->lock);
1330 			fsysPut(fsys);
1331 			return 0;
1332 		}
1333 		vtMemFree(fsys->dev);
1334 		fsys->dev = vtStrDup(argv[0]);
1335 		vtUnlock(fsys->lock);
1336 	}
1337 	else if((fsys = fsysAlloc(name, argv[0])) == nil)
1338 		return 0;
1339 
1340 	fsysPut(fsys);
1341 
1342 	return 1;
1343 }
1344 
1345 static struct {
1346 	char*	cmd;
1347 	int	(*f)(Fsys*, int, char**);
1348 	int	(*f1)(char*, int, char**);
1349 } fsyscmd[] = {
1350 	{ "close",	fsysClose, },
1351 	{ "config",	nil, fsysConfig, },
1352 	{ "open",	nil, fsysOpen, },
1353 	{ "unconfig",	nil, fsysUnconfig, },
1354 	{ "venti",	nil, fsysVenti, },
1355 
1356 	{ "bfree",	fsysBfree, },
1357 	{ "block",	fsysBlock, },
1358 	{ "clre",	fsysClre, },
1359 	{ "clri",	fsysClri, },
1360 	{ "clrp",	fsysClrp, },
1361 	{ "create",	fsysCreate, },
1362 	{ "epoch",	fsysEpoch, },
1363 	{ "label",	fsysLabel, },
1364 	{ "remove",	fsysRemove, },
1365 	{ "snap",	fsysSnap, },
1366 	{ "snaptime",	fsysSnapTime, },
1367 	{ "stat",	fsysStat, },
1368 	{ "sync",	fsysSync, },
1369 	{ "wstat",	fsysWstat, },
1370 	{ "vac",	fsysVac, },
1371 
1372 	{ nil,		nil, },
1373 };
1374 
1375 static int
1376 fsysXXX(char* name, int argc, char* argv[])
1377 {
1378 	int i, r;
1379 	Fsys *fsys;
1380 
1381 	for(i = 0; fsyscmd[i].cmd != nil; i++){
1382 		if(strcmp(fsyscmd[i].cmd, argv[0]) == 0)
1383 			break;
1384 	}
1385 
1386 	if(fsyscmd[i].cmd == nil){
1387 		vtSetError("unknown command - '%s'", argv[0]);
1388 		return 0;
1389 	}
1390 
1391 	/* some commands want the name... */
1392 	if(fsyscmd[i].f1 != nil)
1393 		return (*fsyscmd[i].f1)(name, argc, argv);
1394 
1395 	/* ... but most commands want the Fsys */
1396 	if((fsys = _fsysGet(name)) == nil)
1397 		return 0;
1398 
1399 	vtLock(fsys->lock);
1400 	if(fsys->fs == nil){
1401 		vtUnlock(fsys->lock);
1402 		vtSetError(EFsysNotOpen, name);
1403 		fsysPut(fsys);
1404 		return 0;
1405 	}
1406 
1407 	r = (*fsyscmd[i].f)(fsys, argc, argv);
1408 	vtUnlock(fsys->lock);
1409 	fsysPut(fsys);
1410 	return r;
1411 }
1412 
1413 static int
1414 cmdFsysXXX(int argc, char* argv[])
1415 {
1416 	char *name;
1417 
1418 	if((name = sbox.curfsys) == nil){
1419 		vtSetError(EFsysNoCurrent, argv[0]);
1420 		return 0;
1421 	}
1422 
1423 	return fsysXXX(name, argc, argv);
1424 }
1425 
1426 static int
1427 cmdFsys(int argc, char* argv[])
1428 {
1429 	Fsys *fsys;
1430 	char *usage = "usage: fsys [name ...]";
1431 
1432 	ARGBEGIN{
1433 	default:
1434 		return cliError(usage);
1435 	}ARGEND
1436 
1437 	if(argc == 0){
1438 		vtRLock(sbox.lock);
1439 		for(fsys = sbox.head; fsys != nil; fsys = fsys->next)
1440 			consPrint("\t%s\n", fsys->name);
1441 		vtRUnlock(sbox.lock);
1442 		return 1;
1443 	}
1444 	if(argc == 1){
1445 		if((fsys = fsysGet(argv[0])) == nil)
1446 			return 0;
1447 		sbox.curfsys = vtStrDup(fsys->name);
1448 		consPrompt(sbox.curfsys);
1449 		fsysPut(fsys);
1450 		return 1;
1451 	}
1452 
1453 	return fsysXXX(argv[0], argc-1, argv+1);
1454 }
1455 
1456 int
1457 fsysInit(void)
1458 {
1459 	int i;
1460 
1461 	fmtinstall('H', encodefmt);
1462 	fmtinstall('V', scoreFmt);
1463 	fmtinstall('R', vtErrFmt);
1464 	fmtinstall('L', labelFmt);
1465 
1466 	sbox.lock = vtLockAlloc();
1467 
1468 	cliAddCmd("fsys", cmdFsys);
1469 	for(i = 0; fsyscmd[i].cmd != nil; i++){
1470 		if(fsyscmd[i].f != nil)
1471 			cliAddCmd(fsyscmd[i].cmd, cmdFsysXXX);
1472 	}
1473 	/* the venti cmd is special: the fs can be either open or closed */
1474 	cliAddCmd("venti", cmdFsysXXX);
1475 	cliAddCmd("printconfig", cmdPrintConfig);
1476 
1477 	return 1;
1478 }
1479