xref: /plan9-contrib/sys/src/cmd/fossil/9fsys.c (revision d7aba6c3b511bc618cf0c53345848188fc02611a)
1 #include "stdinc.h"
2 #include <bio.h>
3 #include "dat.h"
4 #include "fns.h"
5 #include "9.h"
6 
7 struct Fsys {
8 	QLock	lock;
9 
10 	char*	name;		/* copy here & Fs to ease error reporting */
11 	char*	dev;
12 	char*	venti;
13 
14 	Fs*	fs;
15 	VtConn* session;
16 	int	ref;
17 
18 	int	noauth;
19 	int	noperm;
20 	int	wstatallow;
21 
22 	Fsys*	next;
23 };
24 
25 int mempcnt;			/* from fossil.c */
26 
27 int	fsGetBlockSize(Fs *fs);
28 
29 static struct {
30 	RWLock	lock;
31 	Fsys*	head;
32 	Fsys*	tail;
33 
34 	char*	curfsys;
35 } sbox;
36 
37 static char *_argv0;
38 #define argv0 _argv0
39 
40 static char FsysAll[] = "all";
41 
42 static char EFsysBusy[] = "fsys: '%s' busy";
43 static char EFsysExists[] = "fsys: '%s' already exists";
44 static char EFsysNoCurrent[] = "fsys: no current fsys";
45 static char EFsysNotFound[] = "fsys: '%s' not found";
46 static char EFsysNotOpen[] = "fsys: '%s' not open";
47 
48 static char *
ventihost(char * host)49 ventihost(char *host)
50 {
51 	if(host != nil)
52 		return vtstrdup(host);
53 	host = getenv("venti");
54 	if(host == nil)
55 		host = vtstrdup("$venti");
56 	return host;
57 }
58 
59 static void
prventihost(char * host)60 prventihost(char *host)
61 {
62 	char *vh;
63 
64 	vh = ventihost(host);
65 	fprint(2, "%s: dialing venti at %s\n",
66 		argv0, netmkaddr(vh, 0, "venti"));
67 	free(vh);
68 }
69 
70 static VtConn *
myDial(char * host)71 myDial(char *host)
72 {
73 	prventihost(host);
74 	return vtdial(host);
75 }
76 
77 static int
myRedial(VtConn * z,char * host)78 myRedial(VtConn *z, char *host)
79 {
80 	prventihost(host);
81 	return vtredial(z, host);
82 }
83 
84 static Fsys*
_fsysGet(char * name)85 _fsysGet(char* name)
86 {
87 	Fsys *fsys;
88 
89 	if(name == nil || name[0] == '\0')
90 		name = "main";
91 
92 	rlock(&sbox.lock);
93 	for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
94 		if(strcmp(name, fsys->name) == 0){
95 			fsys->ref++;
96 			break;
97 		}
98 	}
99 	runlock(&sbox.lock);
100 	if(fsys == nil)
101 		werrstr(EFsysNotFound, name);
102 	return fsys;
103 }
104 
105 static int
cmdPrintConfig(int argc,char * argv[])106 cmdPrintConfig(int argc, char* argv[])
107 {
108 	Fsys *fsys;
109 	char *usage = "usage: printconfig";
110 
111 	ARGBEGIN{
112 	default:
113 		return cliError(usage);
114 	}ARGEND
115 
116 	if(argc)
117 		return cliError(usage);
118 
119 	rlock(&sbox.lock);
120 	for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
121 		consPrint("\tfsys %s config %s\n", fsys->name, fsys->dev);
122 		if(fsys->venti && fsys->venti[0])
123 			consPrint("\tfsys %s venti %q\n", fsys->name,
124 				fsys->venti);
125 	}
126 	runlock(&sbox.lock);
127 	return 1;
128 }
129 
130 Fsys*
fsysGet(char * name)131 fsysGet(char* name)
132 {
133 	Fsys *fsys;
134 
135 	if((fsys = _fsysGet(name)) == nil)
136 		return nil;
137 
138 	qlock(&fsys->lock);
139 	if(fsys->fs == nil){
140 		werrstr(EFsysNotOpen, fsys->name);
141 		qunlock(&fsys->lock);
142 		fsysPut(fsys);
143 		return nil;
144 	}
145 	qunlock(&fsys->lock);
146 
147 	return fsys;
148 }
149 
150 char*
fsysGetName(Fsys * fsys)151 fsysGetName(Fsys* fsys)
152 {
153 	return fsys->name;
154 }
155 
156 Fsys*
fsysIncRef(Fsys * fsys)157 fsysIncRef(Fsys* fsys)
158 {
159 	wlock(&sbox.lock);
160 	fsys->ref++;
161 	wunlock(&sbox.lock);
162 
163 	return fsys;
164 }
165 
166 void
fsysPut(Fsys * fsys)167 fsysPut(Fsys* fsys)
168 {
169 	wlock(&sbox.lock);
170 	assert(fsys->ref > 0);
171 	fsys->ref--;
172 	wunlock(&sbox.lock);
173 }
174 
175 Fs*
fsysGetFs(Fsys * fsys)176 fsysGetFs(Fsys* fsys)
177 {
178 	assert(fsys != nil && fsys->fs != nil);
179 
180 	return fsys->fs;
181 }
182 
183 void
fsysFsRlock(Fsys * fsys)184 fsysFsRlock(Fsys* fsys)
185 {
186 	rlock(&fsys->fs->elk);
187 }
188 
189 void
fsysFsRUnlock(Fsys * fsys)190 fsysFsRUnlock(Fsys* fsys)
191 {
192 	runlock(&fsys->fs->elk);
193 }
194 
195 int
fsysNoAuthCheck(Fsys * fsys)196 fsysNoAuthCheck(Fsys* fsys)
197 {
198 	return fsys->noauth;
199 }
200 
201 int
fsysNoPermCheck(Fsys * fsys)202 fsysNoPermCheck(Fsys* fsys)
203 {
204 	return fsys->noperm;
205 }
206 
207 int
fsysWstatAllow(Fsys * fsys)208 fsysWstatAllow(Fsys* fsys)
209 {
210 	return fsys->wstatallow;
211 }
212 
213 static char modechars[] = "YUGalLdHSATs";
214 static ulong modebits[] = {
215 	ModeSticky,
216 	ModeSetUid,
217 	ModeSetGid,
218 	ModeAppend,
219 	ModeExclusive,
220 	ModeLink,
221 	ModeDir,
222 	ModeHidden,
223 	ModeSystem,
224 	ModeArchive,
225 	ModeTemporary,
226 	ModeSnapshot,
227 	0
228 };
229 
230 char*
fsysModeString(ulong mode,char * buf)231 fsysModeString(ulong mode, char *buf)
232 {
233 	int i;
234 	char *p;
235 
236 	p = buf;
237 	for(i=0; modebits[i]; i++)
238 		if(mode & modebits[i])
239 			*p++ = modechars[i];
240 	sprint(p, "%luo", mode&0777);
241 	return buf;
242 }
243 
244 int
fsysParseMode(char * s,ulong * mode)245 fsysParseMode(char* s, ulong* mode)
246 {
247 	ulong x, y;
248 	char *p;
249 
250 	x = 0;
251 	for(; *s < '0' || *s > '9'; s++){
252 		if(*s == 0)
253 			return 0;
254 		p = strchr(modechars, *s);
255 		if(p == nil)
256 			return 0;
257 		x |= modebits[p-modechars];
258 	}
259 	y = strtoul(s, &p, 8);
260 	if(*p != '\0' || y > 0777)
261 		return 0;
262 	*mode = x|y;
263 	return 1;
264 }
265 
266 File*
fsysGetRoot(Fsys * fsys,char * name)267 fsysGetRoot(Fsys* fsys, char* name)
268 {
269 	File *root, *sub;
270 
271 	assert(fsys != nil && fsys->fs != nil);
272 
273 	root = fsGetRoot(fsys->fs);
274 	if(name == nil || strcmp(name, "") == 0)
275 		return root;
276 
277 	sub = fileWalk(root, name);
278 	fileDecRef(root);
279 
280 	return sub;
281 }
282 
283 static Fsys*
fsysAlloc(char * name,char * dev)284 fsysAlloc(char* name, char* dev)
285 {
286 	Fsys *fsys;
287 
288 	wlock(&sbox.lock);
289 	for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
290 		if(strcmp(fsys->name, name) != 0)
291 			continue;
292 		werrstr(EFsysExists, name);
293 		wunlock(&sbox.lock);
294 		return nil;
295 	}
296 
297 	fsys = vtmallocz(sizeof(Fsys));
298 	fsys->name = vtstrdup(name);
299 	fsys->dev = vtstrdup(dev);
300 
301 	fsys->ref = 1;
302 
303 	if(sbox.tail != nil)
304 		sbox.tail->next = fsys;
305 	else
306 		sbox.head = fsys;
307 	sbox.tail = fsys;
308 	wunlock(&sbox.lock);
309 
310 	return fsys;
311 }
312 
313 static int
fsysClose(Fsys * fsys,int argc,char * argv[])314 fsysClose(Fsys* fsys, int argc, char* argv[])
315 {
316 	char *usage = "usage: [fsys name] close";
317 
318 	ARGBEGIN{
319 	default:
320 		return cliError(usage);
321 	}ARGEND
322 	if(argc)
323 		return cliError(usage);
324 
325 	return cliError("close isn't working yet; halt %s and then kill fossil",
326 		fsys->name);
327 
328 	/*
329 	 * Oooh. This could be hard. What if fsys->ref != 1?
330 	 * Also, fsClose() either does the job or panics, can we
331 	 * gracefully detect it's still busy?
332 	 *
333 	 * More thought and care needed here.
334 	fsClose(fsys->fs);
335 	fsys->fs = nil;
336 	vtfreeconn(fsys->session);
337 	fsys->session = nil;
338 
339 	if(sbox.curfsys != nil && strcmp(fsys->name, sbox.curfsys) == 0){
340 		sbox.curfsys = nil;
341 		consPrompt(nil);
342 	}
343 
344 	return 1;
345 	 */
346 }
347 
348 static int
fsysVac(Fsys * fsys,int argc,char * argv[])349 fsysVac(Fsys* fsys, int argc, char* argv[])
350 {
351 	uchar score[VtScoreSize];
352 	char *usage = "usage: [fsys name] vac path";
353 
354 	ARGBEGIN{
355 	default:
356 		return cliError(usage);
357 	}ARGEND
358 	if(argc != 1)
359 		return cliError(usage);
360 
361 	if(!fsVac(fsys->fs, argv[0], score))
362 		return 0;
363 
364 	consPrint("vac:%V\n", score);
365 	return 1;
366 }
367 
368 static int
fsysSnap(Fsys * fsys,int argc,char * argv[])369 fsysSnap(Fsys* fsys, int argc, char* argv[])
370 {
371 	int doarchive;
372 	char *usage = "usage: [fsys name] snap [-a] [-s /active] [-d /archive/yyyy/mmmm]";
373 	char *src, *dst;
374 
375 	src = nil;
376 	dst = nil;
377 	doarchive = 0;
378 	ARGBEGIN{
379 	default:
380 		return cliError(usage);
381 	case 'a':
382 		doarchive = 1;
383 		break;
384 	case 'd':
385 		if((dst = ARGF()) == nil)
386 			return cliError(usage);
387 		break;
388 	case 's':
389 		if((src = ARGF()) == nil)
390 			return cliError(usage);
391 		break;
392 	}ARGEND
393 	if(argc)
394 		return cliError(usage);
395 
396 	if(!fsSnapshot(fsys->fs, src, dst, doarchive))
397 		return 0;
398 
399 	return 1;
400 }
401 
402 static int
fsysSnapClean(Fsys * fsys,int argc,char * argv[])403 fsysSnapClean(Fsys *fsys, int argc, char* argv[])
404 {
405 	u32int arch, snap, life;
406 	char *usage = "usage: [fsys name] snapclean [maxminutes]\n";
407 
408 	ARGBEGIN{
409 	default:
410 		return cliError(usage);
411 	}ARGEND
412 
413 	if(argc > 1)
414 		return cliError(usage);
415 	if(argc == 1)
416 		life = atoi(argv[0]);
417 	else
418 		snapGetTimes(fsys->fs->snap, &arch, &snap, &life);
419 
420 	fsSnapshotCleanup(fsys->fs, life);
421 	return 1;
422 }
423 
424 static int
fsysSnapTime(Fsys * fsys,int argc,char * argv[])425 fsysSnapTime(Fsys* fsys, int argc, char* argv[])
426 {
427 	char buf[128], *x;
428 	int hh, mm, changed;
429 	u32int arch, snap, life;
430 	char *usage = "usage: [fsys name] snaptime [-a hhmm] [-s snapminutes] [-t maxminutes]";
431 
432 	changed = 0;
433 	snapGetTimes(fsys->fs->snap, &arch, &snap, &life);
434 	ARGBEGIN{
435 	case 'a':
436 		changed = 1;
437 		x = ARGF();
438 		if(x == nil)
439 			return cliError(usage);
440 		if(strcmp(x, "none") == 0){
441 			arch = ~(u32int)0;
442 			break;
443 		}
444 		if(strlen(x) != 4 || strspn(x, "0123456789") != 4)
445 			return cliError(usage);
446 		hh = (x[0]-'0')*10 + x[1]-'0';
447 		mm = (x[2]-'0')*10 + x[3]-'0';
448 		if(hh >= 24 || mm >= 60)
449 			return cliError(usage);
450 		arch = hh*60+mm;
451 		break;
452 	case 's':
453 		changed = 1;
454 		x = ARGF();
455 		if(x == nil)
456 			return cliError(usage);
457 		if(strcmp(x, "none") == 0){
458 			snap = ~(u32int)0;
459 			break;
460 		}
461 		snap = atoi(x);
462 		break;
463 	case 't':
464 		changed = 1;
465 		x = ARGF();
466 		if(x == nil)
467 			return cliError(usage);
468 		if(strcmp(x, "none") == 0){
469 			life = ~(u32int)0;
470 			break;
471 		}
472 		life = atoi(x);
473 		break;
474 	default:
475 		return cliError(usage);
476 	}ARGEND
477 	if(argc > 0)
478 		return cliError(usage);
479 
480 	if(changed){
481 		snapSetTimes(fsys->fs->snap, arch, snap, life);
482 		return 1;
483 	}
484 	snapGetTimes(fsys->fs->snap, &arch, &snap, &life);
485 	if(arch != ~(u32int)0)
486 		sprint(buf, "-a %02d%02d", arch/60, arch%60);
487 	else
488 		sprint(buf, "-a none");
489 	if(snap != ~(u32int)0)
490 		sprint(buf+strlen(buf), " -s %d", snap);
491 	else
492 		sprint(buf+strlen(buf), " -s none");
493 	if(life != ~(u32int)0)
494 		sprint(buf+strlen(buf), " -t %ud", life);
495 	else
496 		sprint(buf+strlen(buf), " -t none");
497 	consPrint("\tsnaptime %s\n", buf);
498 	return 1;
499 }
500 
501 static int
fsysSync(Fsys * fsys,int argc,char * argv[])502 fsysSync(Fsys* fsys, int argc, char* argv[])
503 {
504 	char *usage = "usage: [fsys name] sync";
505 	int n;
506 
507 	ARGBEGIN{
508 	default:
509 		return cliError(usage);
510 	}ARGEND
511 	if(argc > 0)
512 		return cliError(usage);
513 
514 	n = cacheDirty(fsys->fs->cache);
515 	fsSync(fsys->fs);
516 	consPrint("\t%s sync: wrote %d blocks\n", fsys->name, n);
517 	return 1;
518 }
519 
520 static int
fsysHalt(Fsys * fsys,int argc,char * argv[])521 fsysHalt(Fsys *fsys, int argc, char* argv[])
522 {
523 	char *usage = "usage: [fsys name] halt";
524 
525 	ARGBEGIN{
526 	default:
527 		return cliError(usage);
528 	}ARGEND
529 	if(argc > 0)
530 		return cliError(usage);
531 
532 	fsHalt(fsys->fs);
533 	return 1;
534 }
535 
536 static int
fsysUnhalt(Fsys * fsys,int argc,char * argv[])537 fsysUnhalt(Fsys *fsys, int argc, char* argv[])
538 {
539 	char *usage = "usage: [fsys name] unhalt";
540 
541 	ARGBEGIN{
542 	default:
543 		return cliError(usage);
544 	}ARGEND
545 	if(argc > 0)
546 		return cliError(usage);
547 
548 	if(!fsys->fs->halted)
549 		return cliError("file system %s not halted", fsys->name);
550 
551 	fsUnhalt(fsys->fs);
552 	return 1;
553 }
554 
555 static int
fsysRemove(Fsys * fsys,int argc,char * argv[])556 fsysRemove(Fsys* fsys, int argc, char* argv[])
557 {
558 	File *file;
559 	char *usage = "usage: [fsys name] remove path ...";
560 
561 	ARGBEGIN{
562 	default:
563 		return cliError(usage);
564 	}ARGEND
565 	if(argc == 0)
566 		return cliError(usage);
567 
568 	rlock(&fsys->fs->elk);
569 	while(argc > 0){
570 		if((file = fileOpen(fsys->fs, argv[0])) == nil)
571 			consPrint("%s: %r\n", argv[0]);
572 		else{
573 			if(!fileRemove(file, uidadm))
574 				consPrint("%s: %r\n", argv[0]);
575 			fileDecRef(file);
576 		}
577 		argc--;
578 		argv++;
579 	}
580 	runlock(&fsys->fs->elk);
581 
582 	return 1;
583 }
584 
585 static int
fsysClri(Fsys * fsys,int argc,char * argv[])586 fsysClri(Fsys* fsys, int argc, char* argv[])
587 {
588 	char *usage = "usage: [fsys name] clri path ...";
589 
590 	ARGBEGIN{
591 	default:
592 		return cliError(usage);
593 	}ARGEND
594 	if(argc == 0)
595 		return cliError(usage);
596 
597 	rlock(&fsys->fs->elk);
598 	while(argc > 0){
599 		if(!fileClriPath(fsys->fs, argv[0], uidadm))
600 			consPrint("clri %s: %r\n", argv[0]);
601 		argc--;
602 		argv++;
603 	}
604 	runlock(&fsys->fs->elk);
605 
606 	return 1;
607 }
608 
609 /*
610  * Inspect and edit the labels for blocks on disk.
611  */
612 static int
fsysLabel(Fsys * fsys,int argc,char * argv[])613 fsysLabel(Fsys* fsys, int argc, char* argv[])
614 {
615 	Fs *fs;
616 	Label l;
617 	int n, r;
618 	u32int addr;
619 	Block *b, *bb;
620 	char *usage = "usage: [fsys name] label addr [type state epoch epochClose tag]";
621 
622 	ARGBEGIN{
623 	default:
624 		return cliError(usage);
625 	}ARGEND
626 	if(argc != 1 && argc != 6)
627 		return cliError(usage);
628 
629 	r = 0;
630 	rlock(&fsys->fs->elk);
631 
632 	fs = fsys->fs;
633 	addr = strtoul(argv[0], 0, 0);
634 	b = cacheLocal(fs->cache, PartData, addr, OReadOnly);
635 	if(b == nil)
636 		goto Out0;
637 
638 	l = b->l;
639 	consPrint("%slabel %#ux %ud %ud %ud %ud %#x\n",
640 		argc==6 ? "old: " : "", addr, l.type, l.state,
641 		l.epoch, l.epochClose, l.tag);
642 
643 	if(argc == 6){
644 		if(strcmp(argv[1], "-") != 0)
645 			l.type = atoi(argv[1]);
646 		if(strcmp(argv[2], "-") != 0)
647 			l.state = atoi(argv[2]);
648 		if(strcmp(argv[3], "-") != 0)
649 			l.epoch = strtoul(argv[3], 0, 0);
650 		if(strcmp(argv[4], "-") != 0)
651 			l.epochClose = strtoul(argv[4], 0, 0);
652 		if(strcmp(argv[5], "-") != 0)
653 			l.tag = strtoul(argv[5], 0, 0);
654 
655 		consPrint("new: label %#ux %ud %ud %ud %ud %#x\n",
656 			addr, l.type, l.state, l.epoch, l.epochClose, l.tag);
657 		bb = _blockSetLabel(b, &l);
658 		if(bb == nil)
659 			goto Out1;
660 		n = 0;
661 		for(;;){
662 			if(blockWrite(bb, Waitlock)){
663 				while(bb->iostate != BioClean){
664 					assert(bb->iostate == BioWriting);
665 					rsleep(&bb->ioready);
666 				}
667 				break;
668 			}
669 			consPrint("blockWrite: %r\n");
670 			if(n++ >= 5){
671 				consPrint("giving up\n");
672 				break;
673 			}
674 			sleep(5*1000);
675 		}
676 		blockPut(bb);
677 	}
678 	r = 1;
679 Out1:
680 	blockPut(b);
681 Out0:
682 	runlock(&fs->elk);
683 
684 	return r;
685 }
686 
687 /*
688  * Inspect and edit the blocks on disk.
689  */
690 static int
fsysBlock(Fsys * fsys,int argc,char * argv[])691 fsysBlock(Fsys* fsys, int argc, char* argv[])
692 {
693 	Fs *fs;
694 	char *s;
695 	Block *b;
696 	uchar *buf;
697 	u32int addr;
698 	int c, count, i, offset;
699 	char *usage = "usage: [fsys name] block addr offset [count [data]]";
700 
701 	ARGBEGIN{
702 	default:
703 		return cliError(usage);
704 	}ARGEND
705 	if(argc < 2 || argc > 4)
706 		return cliError(usage);
707 
708 	fs = fsys->fs;
709 	addr = strtoul(argv[0], 0, 0);
710 	offset = strtoul(argv[1], 0, 0);
711 	if(offset < 0 || offset >= fs->blockSize){
712 		werrstr("bad offset");
713 		return 0;
714 	}
715 	if(argc > 2)
716 		count = strtoul(argv[2], 0, 0);
717 	else
718 		count = 100000000;
719 	if(offset+count > fs->blockSize)
720 		count = fs->blockSize - count;
721 
722 	rlock(&fs->elk);
723 
724 	b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly);
725 	if(b == nil){
726 		werrstr("cacheLocal %#ux: %r", addr);
727 		runlock(&fs->elk);
728 		return 0;
729 	}
730 
731 	consPrint("\t%sblock %#ux %ud %ud %.*H\n",
732 		argc==4 ? "old: " : "", addr, offset, count, count, b->data+offset);
733 
734 	if(argc == 4){
735 		s = argv[3];
736 		if(strlen(s) != 2*count){
737 			werrstr("bad data count");
738 			goto Out;
739 		}
740 		buf = vtmallocz(count);
741 		for(i = 0; i < count*2; i++){
742 			if(s[i] >= '0' && s[i] <= '9')
743 				c = s[i] - '0';
744 			else if(s[i] >= 'a' && s[i] <= 'f')
745 				c = s[i] - 'a' + 10;
746 			else if(s[i] >= 'A' && s[i] <= 'F')
747 				c = s[i] - 'A' + 10;
748 			else{
749 				werrstr("bad hex");
750 				vtfree(buf);
751 				goto Out;
752 			}
753 			if((i & 1) == 0)
754 				c <<= 4;
755 			buf[i>>1] |= c;
756 		}
757 		memmove(b->data+offset, buf, count);
758 		consPrint("\tnew: block %#ux %ud %ud %.*H\n",
759 			addr, offset, count, count, b->data+offset);
760 		blockDirty(b);
761 	}
762 
763 Out:
764 	blockPut(b);
765 	runlock(&fs->elk);
766 
767 	return 1;
768 }
769 
770 /*
771  * Free a disk block.
772  */
773 static int
fsysBfree(Fsys * fsys,int argc,char * argv[])774 fsysBfree(Fsys* fsys, int argc, char* argv[])
775 {
776 	Fs *fs;
777 	Label l;
778 	char *p;
779 	Block *b;
780 	u32int addr;
781 	char *usage = "usage: [fsys name] bfree addr ...";
782 
783 	ARGBEGIN{
784 	default:
785 		return cliError(usage);
786 	}ARGEND
787 	if(argc == 0)
788 		return cliError(usage);
789 
790 	fs = fsys->fs;
791 	rlock(&fs->elk);
792 	while(argc > 0){
793 		addr = strtoul(argv[0], &p, 0);
794 		if(*p != '\0'){
795 			consPrint("bad address - '%ud'\n", addr);
796 			/* syntax error; let's stop */
797 			runlock(&fs->elk);
798 			return 0;
799 		}
800 		b = cacheLocal(fs->cache, PartData, addr, OReadOnly);
801 		if(b == nil){
802 			consPrint("loading %#ux: %r\n", addr);
803 			continue;
804 		}
805 		l = b->l;
806 		if(l.state == BsFree)
807 			consPrint("%#ux is already free\n", addr);
808 		else{
809 			consPrint("label %#ux %ud %ud %ud %ud %#x\n",
810 				addr, l.type, l.state, l.epoch, l.epochClose, l.tag);
811 			l.state = BsFree;
812 			l.type = BtMax;
813 			l.tag = 0;
814 			l.epoch = 0;
815 			l.epochClose = 0;
816 			if(!blockSetLabel(b, &l, 0))
817 				consPrint("freeing %#ux: %r\n", addr);
818 		}
819 		blockPut(b);
820 		argc--;
821 		argv++;
822 	}
823 	runlock(&fs->elk);
824 
825 	return 1;
826 }
827 
828 static int
fsysDf(Fsys * fsys,int argc,char * argv[])829 fsysDf(Fsys *fsys, int argc, char* argv[])
830 {
831 	char *usage = "usage: [fsys name] df";
832 	u32int used, tot, bsize;
833 	Fs *fs;
834 
835 	ARGBEGIN{
836 	default:
837 		return cliError(usage);
838 	}ARGEND
839 	if(argc != 0)
840 		return cliError(usage);
841 
842 	fs = fsys->fs;
843 	cacheCountUsed(fs->cache, fs->elo, &used, &tot, &bsize);
844 	consPrint("\t%s: %,llud used + %,llud free = %,llud (%.1f%% used)\n",
845 		fsys->name, used*(vlong)bsize, (tot-used)*(vlong)bsize,
846 		tot*(vlong)bsize, used*100.0/tot);
847 	return 1;
848 }
849 
850 /*
851  * Zero an entry or a pointer.
852  */
853 static int
fsysClrep(Fsys * fsys,int argc,char * argv[],int ch)854 fsysClrep(Fsys* fsys, int argc, char* argv[], int ch)
855 {
856 	Fs *fs;
857 	Entry e;
858 	Block *b;
859 	u32int addr;
860 	int i, max, offset, sz;
861 	uchar zero[VtEntrySize];
862 	char *usage = "usage: [fsys name] clr%c addr offset ...";
863 
864 	ARGBEGIN{
865 	default:
866 		return cliError(usage, ch);
867 	}ARGEND
868 	if(argc < 2)
869 		return cliError(usage, ch);
870 
871 	fs = fsys->fs;
872 	rlock(&fsys->fs->elk);
873 
874 	addr = strtoul(argv[0], 0, 0);
875 	b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly);
876 	if(b == nil){
877 		werrstr("cacheLocal %#ux: %r", addr);
878 	Err:
879 		runlock(&fsys->fs->elk);
880 		return 0;
881 	}
882 
883 	switch(ch){
884 	default:
885 		werrstr("clrep");
886 		goto Err;
887 	case 'e':
888 		if(b->l.type != BtDir){
889 			werrstr("wrong block type");
890 			goto Err;
891 		}
892 		sz = VtEntrySize;
893 		memset(&e, 0, sizeof e);
894 		entryPack(&e, zero, 0);
895 		break;
896 	case 'p':
897 		if(b->l.type == BtDir || b->l.type == BtData){
898 			werrstr("wrong block type");
899 			goto Err;
900 		}
901 		sz = VtScoreSize;
902 		memmove(zero, vtzeroscore, VtScoreSize);
903 		break;
904 	}
905 	max = fs->blockSize/sz;
906 
907 	for(i = 1; i < argc; i++){
908 		offset = atoi(argv[i]);
909 		if(offset >= max){
910 			consPrint("\toffset %d too large (>= %d)\n", i, max);
911 			continue;
912 		}
913 		consPrint("\tblock %#ux %d %d %.*H\n", addr, offset*sz, sz, sz, b->data+offset*sz);
914 		memmove(b->data+offset*sz, zero, sz);
915 	}
916 	blockDirty(b);
917 	blockPut(b);
918 	runlock(&fsys->fs->elk);
919 
920 	return 1;
921 }
922 
923 static int
fsysClre(Fsys * fsys,int argc,char * argv[])924 fsysClre(Fsys* fsys, int argc, char* argv[])
925 {
926 	return fsysClrep(fsys, argc, argv, 'e');
927 }
928 
929 static int
fsysClrp(Fsys * fsys,int argc,char * argv[])930 fsysClrp(Fsys* fsys, int argc, char* argv[])
931 {
932 	return fsysClrep(fsys, argc, argv, 'p');
933 }
934 
935 static int
fsysEsearch1(File * f,char * s,u32int elo)936 fsysEsearch1(File* f, char* s, u32int elo)
937 {
938 	int n, r;
939 	DirEntry de;
940 	DirEntryEnum *dee;
941 	File *ff;
942 	Entry e, ee;
943 	char *t;
944 
945 	dee = deeOpen(f);
946 	if(dee == nil)
947 		return 0;
948 
949 	n = 0;
950 	for(;;){
951 		r = deeRead(dee, &de);
952 		if(r < 0){
953 			consPrint("\tdeeRead %s/%s: %r\n", s, de.elem);
954 			break;
955 		}
956 		if(r == 0)
957 			break;
958 		if(de.mode & ModeSnapshot){
959 			if((ff = fileWalk(f, de.elem)) == nil)
960 				consPrint("\tcannot walk %s/%s: %r\n", s, de.elem);
961 			else{
962 				if(!fileGetSources(ff, &e, &ee))
963 					consPrint("\tcannot get sources for %s/%s: %r\n", s, de.elem);
964 				else if(e.snap != 0 && e.snap < elo){
965 					consPrint("\t%ud\tclri %s/%s\n", e.snap, s, de.elem);
966 					n++;
967 				}
968 				fileDecRef(ff);
969 			}
970 		}
971 		else if(de.mode & ModeDir){
972 			if((ff = fileWalk(f, de.elem)) == nil)
973 				consPrint("\tcannot walk %s/%s: %r\n", s, de.elem);
974 			else{
975 				t = smprint("%s/%s", s, de.elem);
976 				n += fsysEsearch1(ff, t, elo);
977 				vtfree(t);
978 				fileDecRef(ff);
979 			}
980 		}
981 		deCleanup(&de);
982 		if(r < 0)
983 			break;
984 	}
985 	deeClose(dee);
986 
987 	return n;
988 }
989 
990 static int
fsysEsearch(Fs * fs,char * path,u32int elo)991 fsysEsearch(Fs* fs, char* path, u32int elo)
992 {
993 	int n;
994 	File *f;
995 	DirEntry de;
996 
997 	f = fileOpen(fs, path);
998 	if(f == nil)
999 		return 0;
1000 	if(!fileGetDir(f, &de)){
1001 		consPrint("\tfileGetDir %s failed: %r\n", path);
1002 		fileDecRef(f);
1003 		return 0;
1004 	}
1005 	if((de.mode & ModeDir) == 0){
1006 		fileDecRef(f);
1007 		deCleanup(&de);
1008 		return 0;
1009 	}
1010 	deCleanup(&de);
1011 	n = fsysEsearch1(f, path, elo);
1012 	fileDecRef(f);
1013 	return n;
1014 }
1015 
1016 static int
fsysEpoch(Fsys * fsys,int argc,char * argv[])1017 fsysEpoch(Fsys* fsys, int argc, char* argv[])
1018 {
1019 	Fs *fs;
1020 	int force, n, remove;
1021 	u32int low, old;
1022 	char *usage = "usage: [fsys name] epoch [[-ry] low]";
1023 
1024 	force = 0;
1025 	remove = 0;
1026 	ARGBEGIN{
1027 	case 'y':
1028 		force = 1;
1029 		break;
1030 	case 'r':
1031 		remove = 1;
1032 		break;
1033 	default:
1034 		return cliError(usage);
1035 	}ARGEND
1036 	if(argc > 1)
1037 		return cliError(usage);
1038 	if(argc > 0)
1039 		low = strtoul(argv[0], 0, 0);
1040 	else
1041 		low = ~(u32int)0;
1042 
1043 	if(low == 0)
1044 		return cliError("low epoch cannot be zero");
1045 
1046 	fs = fsys->fs;
1047 
1048 	rlock(&fs->elk);
1049 	consPrint("\tlow %ud hi %ud\n", fs->elo, fs->ehi);
1050 	if(low == ~(u32int)0){
1051 		runlock(&fs->elk);
1052 		return 1;
1053 	}
1054 	n = fsysEsearch(fsys->fs, "/archive", low);
1055 	n += fsysEsearch(fsys->fs, "/snapshot", low);
1056 	consPrint("\t%d snapshot%s found with epoch < %ud\n", n, n==1 ? "" : "s", low);
1057 	runlock(&fs->elk);
1058 
1059 	/*
1060 	 * There's a small race here -- a new snapshot with epoch < low might
1061 	 * get introduced now that we unlocked fs->elk.  Low has to
1062 	 * be <= fs->ehi.  Of course, in order for this to happen low has
1063 	 * to be equal to the current fs->ehi _and_ a snapshot has to
1064 	 * run right now.  This is a small enough window that I don't care.
1065 	 */
1066 	if(n != 0 && !force){
1067 		consPrint("\tnot setting low epoch\n");
1068 		return 1;
1069 	}
1070 	old = fs->elo;
1071 	if(!fsEpochLow(fs, low))
1072 		consPrint("\tfsEpochLow: %r\n");
1073 	else{
1074 		consPrint("\told: epoch%s %ud\n", force ? " -y" : "", old);
1075 		consPrint("\tnew: epoch%s %ud\n", force ? " -y" : "", fs->elo);
1076 		if(fs->elo < low)
1077 			consPrint("\twarning: new low epoch < old low epoch\n");
1078 		if(force && remove)
1079 			fsSnapshotRemove(fs);
1080 	}
1081 
1082 	return 1;
1083 }
1084 
1085 static int
fsysCreate(Fsys * fsys,int argc,char * argv[])1086 fsysCreate(Fsys* fsys, int argc, char* argv[])
1087 {
1088 	int r;
1089 	ulong mode;
1090 	char *elem, *p, *path;
1091 	char *usage = "usage: [fsys name] create path uid gid perm";
1092 	DirEntry de;
1093 	File *file, *parent;
1094 
1095 	ARGBEGIN{
1096 	default:
1097 		return cliError(usage);
1098 	}ARGEND
1099 	if(argc != 4)
1100 		return cliError(usage);
1101 
1102 	if(!fsysParseMode(argv[3], &mode))
1103 		return cliError(usage);
1104 	if(mode&ModeSnapshot)
1105 		return cliError("create - cannot create with snapshot bit set");
1106 
1107 	if(strcmp(argv[1], uidnoworld) == 0)
1108 		return cliError("permission denied");
1109 
1110 	rlock(&fsys->fs->elk);
1111 	path = vtstrdup(argv[0]);
1112 	if((p = strrchr(path, '/')) != nil){
1113 		*p++ = '\0';
1114 		elem = p;
1115 		p = path;
1116 		if(*p == '\0')
1117 			p = "/";
1118 	}
1119 	else{
1120 		p = "/";
1121 		elem = path;
1122 	}
1123 
1124 	r = 0;
1125 	if((parent = fileOpen(fsys->fs, p)) == nil)
1126 		goto out;
1127 
1128 	file = fileCreate(parent, elem, mode, argv[1]);
1129 	fileDecRef(parent);
1130 	if(file == nil){
1131 		werrstr("create %s/%s: %r", p, elem);
1132 		goto out;
1133 	}
1134 
1135 	if(!fileGetDir(file, &de)){
1136 		werrstr("stat failed after create: %r");
1137 		goto out1;
1138 	}
1139 
1140 	if(strcmp(de.gid, argv[2]) != 0){
1141 		vtfree(de.gid);
1142 		de.gid = vtstrdup(argv[2]);
1143 		if(!fileSetDir(file, &de, argv[1])){
1144 			werrstr("wstat failed after create: %r");
1145 			goto out2;
1146 		}
1147 	}
1148 	r = 1;
1149 
1150 out2:
1151 	deCleanup(&de);
1152 out1:
1153 	fileDecRef(file);
1154 out:
1155 	vtfree(path);
1156 	runlock(&fsys->fs->elk);
1157 
1158 	return r;
1159 }
1160 
1161 static void
fsysPrintStat(char * prefix,char * file,DirEntry * de)1162 fsysPrintStat(char *prefix, char *file, DirEntry *de)
1163 {
1164 	char buf[64];
1165 
1166 	if(prefix == nil)
1167 		prefix = "";
1168 	consPrint("%sstat %q %q %q %q %s %llud\n", prefix,
1169 		file, de->elem, de->uid, de->gid, fsysModeString(de->mode, buf), de->size);
1170 }
1171 
1172 static int
fsysStat(Fsys * fsys,int argc,char * argv[])1173 fsysStat(Fsys* fsys, int argc, char* argv[])
1174 {
1175 	int i;
1176 	File *f;
1177 	DirEntry de;
1178 	char *usage = "usage: [fsys name] stat files...";
1179 
1180 	ARGBEGIN{
1181 	default:
1182 		return cliError(usage);
1183 	}ARGEND
1184 
1185 	if(argc == 0)
1186 		return cliError(usage);
1187 
1188 	rlock(&fsys->fs->elk);
1189 	for(i=0; i<argc; i++){
1190 		if((f = fileOpen(fsys->fs, argv[i])) == nil){
1191 			consPrint("%s: %r\n", argv[i]);
1192 			continue;
1193 		}
1194 		if(!fileGetDir(f, &de)){
1195 			consPrint("%s: %r\n", argv[i]);
1196 			fileDecRef(f);
1197 			continue;
1198 		}
1199 		fsysPrintStat("\t", argv[i], &de);
1200 		deCleanup(&de);
1201 		fileDecRef(f);
1202 	}
1203 	runlock(&fsys->fs->elk);
1204 	return 1;
1205 }
1206 
1207 static int
fsysWstat(Fsys * fsys,int argc,char * argv[])1208 fsysWstat(Fsys *fsys, int argc, char* argv[])
1209 {
1210 	File *f;
1211 	char *p;
1212 	DirEntry de;
1213 	char *usage = "usage: [fsys name] wstat file elem uid gid mode length\n"
1214 		"\tuse - for any field to mean don't change";
1215 
1216 	ARGBEGIN{
1217 	default:
1218 		return cliError(usage);
1219 	}ARGEND
1220 
1221 	if(argc != 6)
1222 		return cliError(usage);
1223 
1224 	rlock(&fsys->fs->elk);
1225 	if((f = fileOpen(fsys->fs, argv[0])) == nil){
1226 		werrstr("console wstat - walk - %r");
1227 		runlock(&fsys->fs->elk);
1228 		return 0;
1229 	}
1230 	if(!fileGetDir(f, &de)){
1231 		werrstr("console wstat - stat - %r");
1232 		fileDecRef(f);
1233 		runlock(&fsys->fs->elk);
1234 		return 0;
1235 	}
1236 	fsysPrintStat("\told: w", argv[0], &de);
1237 
1238 	if(strcmp(argv[1], "-") != 0){
1239 		if(!validFileName(argv[1])){
1240 			werrstr("console wstat - bad elem");
1241 			goto error;
1242 		}
1243 		vtfree(de.elem);
1244 		de.elem = vtstrdup(argv[1]);
1245 	}
1246 	if(strcmp(argv[2], "-") != 0){
1247 		if(!validUserName(argv[2])){
1248 			werrstr("console wstat - bad uid");
1249 			goto error;
1250 		}
1251 		vtfree(de.uid);
1252 		de.uid = vtstrdup(argv[2]);
1253 	}
1254 	if(strcmp(argv[3], "-") != 0){
1255 		if(!validUserName(argv[3])){
1256 			werrstr("console wstat - bad gid");
1257 			goto error;
1258 		}
1259 		vtfree(de.gid);
1260 		de.gid = vtstrdup(argv[3]);
1261 	}
1262 	if(strcmp(argv[4], "-") != 0){
1263 		if(!fsysParseMode(argv[4], &de.mode)){
1264 			werrstr("console wstat - bad mode");
1265 			goto error;
1266 		}
1267 	}
1268 	if(strcmp(argv[5], "-") != 0){
1269 		de.size = strtoull(argv[5], &p, 0);
1270 		if(argv[5][0] == '\0' || *p != '\0' || (vlong)de.size < 0){
1271 			werrstr("console wstat - bad length");
1272 			goto error;
1273 		}
1274 	}
1275 
1276 	if(!fileSetDir(f, &de, uidadm)){
1277 		werrstr("console wstat - %r");
1278 		goto error;
1279 	}
1280 	deCleanup(&de);
1281 
1282 	if(!fileGetDir(f, &de)){
1283 		werrstr("console wstat - stat2 - %r");
1284 		goto error;
1285 	}
1286 	fsysPrintStat("\tnew: w", argv[0], &de);
1287 	deCleanup(&de);
1288 	fileDecRef(f);
1289 	runlock(&fsys->fs->elk);
1290 
1291 	return 1;
1292 
1293 error:
1294 	deCleanup(&de);	/* okay to do this twice */
1295 	fileDecRef(f);
1296 	runlock(&fsys->fs->elk);
1297 	return 0;
1298 }
1299 
1300 static void
fsckClri(Fsck * fsck,char * name,MetaBlock * mb,int i,Block * b)1301 fsckClri(Fsck *fsck, char *name, MetaBlock *mb, int i, Block *b)
1302 {
1303 	USED(name);
1304 
1305 	if((fsck->flags&DoClri) == 0)
1306 		return;
1307 
1308 	mbDelete(mb, i);
1309 	mbPack(mb);
1310 	blockDirty(b);
1311 }
1312 
1313 static void
fsckClose(Fsck * fsck,Block * b,u32int epoch)1314 fsckClose(Fsck *fsck, Block *b, u32int epoch)
1315 {
1316 	Label l;
1317 
1318 	if((fsck->flags&DoClose) == 0)
1319 		return;
1320 	l = b->l;
1321 	if(l.state == BsFree || (l.state&BsClosed)){
1322 		consPrint("%#ux is already closed\n", b->addr);
1323 		return;
1324 	}
1325 	if(epoch){
1326 		l.state |= BsClosed;
1327 		l.epochClose = epoch;
1328 	}else
1329 		l.state = BsFree;
1330 
1331 	if(!blockSetLabel(b, &l, 0))
1332 		consPrint("%#ux setlabel: %r\n", b->addr);
1333 }
1334 
1335 static void
fsckClre(Fsck * fsck,Block * b,int offset)1336 fsckClre(Fsck *fsck, Block *b, int offset)
1337 {
1338 	Entry e;
1339 
1340 	if((fsck->flags&DoClre) == 0)
1341 		return;
1342 	if(offset<0 || offset*VtEntrySize >= fsck->bsize){
1343 		consPrint("bad clre\n");
1344 		return;
1345 	}
1346 	memset(&e, 0, sizeof e);
1347 	entryPack(&e, b->data, offset);
1348 	blockDirty(b);
1349 }
1350 
1351 static void
fsckClrp(Fsck * fsck,Block * b,int offset)1352 fsckClrp(Fsck *fsck, Block *b, int offset)
1353 {
1354 	if((fsck->flags&DoClrp) == 0)
1355 		return;
1356 	if(offset<0 || offset*VtScoreSize >= fsck->bsize){
1357 		consPrint("bad clre\n");
1358 		return;
1359 	}
1360 	memmove(b->data+offset*VtScoreSize, vtzeroscore, VtScoreSize);
1361 	blockDirty(b);
1362 }
1363 
1364 static int
fsysCheck(Fsys * fsys,int argc,char * argv[])1365 fsysCheck(Fsys *fsys, int argc, char *argv[])
1366 {
1367 	int i, halting;
1368 	char *usage = "usage: [fsys name] check [-v] [options]";
1369 	Fsck fsck;
1370 	Block *b;
1371 	Super super;
1372 
1373 	memset(&fsck, 0, sizeof fsck);
1374 	fsck.fs = fsys->fs;
1375 	fsck.clri = fsckClri;
1376 	fsck.clre = fsckClre;
1377 	fsck.clrp = fsckClrp;
1378 	fsck.close = fsckClose;
1379 	fsck.print = consPrint;
1380 
1381 	ARGBEGIN{
1382 	default:
1383 		return cliError(usage);
1384 	}ARGEND
1385 
1386 	for(i=0; i<argc; i++){
1387 		if(strcmp(argv[i], "pblock") == 0)
1388 			fsck.printblocks = 1;
1389 		else if(strcmp(argv[i], "pdir") == 0)
1390 			fsck.printdirs = 1;
1391 		else if(strcmp(argv[i], "pfile") == 0)
1392 			fsck.printfiles = 1;
1393 		else if(strcmp(argv[i], "bclose") == 0)
1394 			fsck.flags |= DoClose;
1395 		else if(strcmp(argv[i], "clri") == 0)
1396 			fsck.flags |= DoClri;
1397 		else if(strcmp(argv[i], "clre") == 0)
1398 			fsck.flags |= DoClre;
1399 		else if(strcmp(argv[i], "clrp") == 0)
1400 			fsck.flags |= DoClrp;
1401 		else if(strcmp(argv[i], "fix") == 0)
1402 			fsck.flags |= DoClose|DoClri|DoClre|DoClrp;
1403 		else if(strcmp(argv[i], "venti") == 0)
1404 			fsck.useventi = 1;
1405 		else if(strcmp(argv[i], "snapshot") == 0)
1406 			fsck.walksnapshots = 1;
1407 		else{
1408 			consPrint("unknown option '%s'\n", argv[i]);
1409 			return cliError(usage);
1410 		}
1411 	}
1412 
1413 	halting = fsys->fs->halted==0;
1414 	if(halting)
1415 		fsHalt(fsys->fs);
1416 	if(fsys->fs->arch){
1417 		b = superGet(fsys->fs->cache, &super);
1418 		if(b == nil){
1419 			consPrint("could not load super block\n");
1420 			goto Out;
1421 		}
1422 		blockPut(b);
1423 		if(super.current != NilBlock){
1424 			consPrint("cannot check fs while archiver is running; "
1425 				"wait for it to finish\n");
1426 			goto Out;
1427 		}
1428 	}
1429 	fsCheck(&fsck);
1430 	consPrint("fsck: %d clri, %d clre, %d clrp, %d bclose\n",
1431 		fsck.nclri, fsck.nclre, fsck.nclrp, fsck.nclose);
1432 Out:
1433 	if(halting)
1434 		fsUnhalt(fsys->fs);
1435 	return 1;
1436 }
1437 
1438 static int
fsysVenti(char * name,int argc,char * argv[])1439 fsysVenti(char* name, int argc, char* argv[])
1440 {
1441 	int r;
1442 	char *host;
1443 	char *usage = "usage: [fsys name] venti [address]";
1444 	Fsys *fsys;
1445 
1446 	ARGBEGIN{
1447 	default:
1448 		return cliError(usage);
1449 	}ARGEND
1450 
1451 	if(argc == 0)
1452 		host = nil;
1453 	else if(argc == 1)
1454 		host = argv[0];
1455 	else
1456 		return cliError(usage);
1457 
1458 	if((fsys = _fsysGet(name)) == nil)
1459 		return 0;
1460 
1461 	qlock(&fsys->lock);
1462 	if(host == nil)
1463 		host = fsys->venti;
1464 	else{
1465 		vtfree(fsys->venti);
1466 		if(host[0])
1467 			fsys->venti = vtstrdup(host);
1468 		else{
1469 			host = nil;
1470 			fsys->venti = nil;
1471 		}
1472 	}
1473 
1474 	/* already open: do a redial */
1475 	if(fsys->fs != nil){
1476 		if(fsys->session == nil){
1477 			werrstr("file system was opened with -V");
1478 			r = 0;
1479 			goto out;
1480 		}
1481 		r = 1;
1482 		if(myRedial(fsys->session, host) < 0
1483 		|| vtconnect(fsys->session) < 0)
1484 			r = 0;
1485 		goto out;
1486 	}
1487 
1488 	/* not yet open: try to dial */
1489 	if(fsys->session)
1490 		vtfreeconn(fsys->session);
1491 	r = 1;
1492 	if((fsys->session = myDial(host)) == nil
1493 	|| vtconnect(fsys->session) < 0)
1494 		r = 0;
1495 out:
1496 	qunlock(&fsys->lock);
1497 	fsysPut(fsys);
1498 	return r;
1499 }
1500 
1501 static ulong
freemem(void)1502 freemem(void)
1503 {
1504 	int nf, pgsize = 0;
1505 	uvlong size, userpgs = 0, userused = 0;
1506 	char *ln, *sl;
1507 	char *fields[2];
1508 	Biobuf *bp;
1509 
1510 	size = 64*1024*1024;
1511 	bp = Bopen("#c/swap", OREAD);
1512 	if (bp != nil) {
1513 		while ((ln = Brdline(bp, '\n')) != nil) {
1514 			ln[Blinelen(bp)-1] = '\0';
1515 			nf = tokenize(ln, fields, nelem(fields));
1516 			if (nf != 2)
1517 				continue;
1518 			if (strcmp(fields[1], "pagesize") == 0)
1519 				pgsize = atoi(fields[0]);
1520 			else if (strcmp(fields[1], "user") == 0) {
1521 				sl = strchr(fields[0], '/');
1522 				if (sl == nil)
1523 					continue;
1524 				userpgs = atoll(sl+1);
1525 				userused = atoll(fields[0]);
1526 			}
1527 		}
1528 		Bterm(bp);
1529 		if (pgsize > 0 && userpgs > 0)
1530 			size = (userpgs - userused) * pgsize;
1531 	}
1532 	/* cap it to keep the size within 32 bits */
1533 	if (size >= 3840UL * 1024 * 1024)
1534 		size = 3840UL * 1024 * 1024;
1535 	return size;
1536 }
1537 
1538 static int
fsysOpen(char * name,int argc,char * argv[])1539 fsysOpen(char* name, int argc, char* argv[])
1540 {
1541 	char *p, *host;
1542 	Fsys *fsys;
1543 	int noauth, noventi, noperm, rflag, wstatallow, noatimeupd;
1544 	long ncache;
1545 	char *usage = "usage: fsys name open [-APVWr] [-c ncache]";
1546 
1547 	ncache = 1000;
1548 	noauth = noperm = wstatallow = noventi = noatimeupd = 0;
1549 	rflag = OReadWrite;
1550 
1551 	ARGBEGIN{
1552 	default:
1553 		return cliError(usage);
1554 	case 'A':
1555 		noauth = 1;
1556 		break;
1557 	case 'P':
1558 		noperm = 1;
1559 		break;
1560 	case 'V':
1561 		noventi = 1;
1562 		break;
1563 	case 'W':
1564 		wstatallow = 1;
1565 		break;
1566 	case 'a':
1567 		noatimeupd = 1;
1568 		break;
1569 	case 'c':
1570 		p = ARGF();
1571 		if(p == nil)
1572 			return cliError(usage);
1573 		ncache = strtol(argv[0], &p, 0);
1574 		if(ncache <= 0 || p == argv[0] || *p != '\0')
1575 			return cliError(usage);
1576 		break;
1577 	case 'r':
1578 		rflag = OReadOnly;
1579 		break;
1580 	}ARGEND
1581 	if(argc)
1582 		return cliError(usage);
1583 
1584 	if((fsys = _fsysGet(name)) == nil)
1585 		return 0;
1586 
1587 	/* automatic memory sizing? */
1588 	if(mempcnt > 0) {
1589 		/* TODO: 8K is a hack; use the actual block size */
1590 		ncache = (((vlong)freemem() * mempcnt) / 100) / (8*1024);
1591 		if (ncache < 100)
1592 			ncache = 100;
1593 	}
1594 
1595 	qlock(&fsys->lock);
1596 	if(fsys->fs != nil){
1597 		werrstr(EFsysBusy, fsys->name);
1598 		qunlock(&fsys->lock);
1599 		fsysPut(fsys);
1600 		return 0;
1601 	}
1602 
1603 	if(noventi){
1604 		if(fsys->session){
1605 			vtfreeconn(fsys->session);
1606 			fsys->session = nil;
1607 		}
1608 	}
1609 	else if(fsys->session == nil){
1610 		if(fsys->venti && fsys->venti[0])
1611 			host = fsys->venti;
1612 		else
1613 			host = nil;
1614 
1615 		if((fsys->session = myDial(host)) == nil
1616 		|| vtconnect(fsys->session) < 0 && !noventi)
1617 			fprint(2, "warning: connecting to venti: %r\n");
1618 	}
1619 	if((fsys->fs = fsOpen(fsys->dev, fsys->session, ncache, rflag)) == nil){
1620 		werrstr("fsOpen: %r");
1621 		qunlock(&fsys->lock);
1622 		fsysPut(fsys);
1623 		return 0;
1624 	}
1625 	fsys->fs->name = fsys->name;	/* for better error messages */
1626 	fsys->noauth = noauth;
1627 	fsys->noperm = noperm;
1628 	fsys->wstatallow = wstatallow;
1629 	fsys->fs->noatimeupd = noatimeupd;
1630 	qunlock(&fsys->lock);
1631 	fsysPut(fsys);
1632 
1633 	if(strcmp(name, "main") == 0)
1634 		usersFileRead(nil);
1635 
1636 	return 1;
1637 }
1638 
1639 static int
fsysUnconfig(char * name,int argc,char * argv[])1640 fsysUnconfig(char* name, int argc, char* argv[])
1641 {
1642 	Fsys *fsys, **fp;
1643 	char *usage = "usage: fsys name unconfig";
1644 
1645 	ARGBEGIN{
1646 	default:
1647 		return cliError(usage);
1648 	}ARGEND
1649 	if(argc)
1650 		return cliError(usage);
1651 
1652 	wlock(&sbox.lock);
1653 	fp = &sbox.head;
1654 	for(fsys = *fp; fsys != nil; fsys = fsys->next){
1655 		if(strcmp(fsys->name, name) == 0)
1656 			break;
1657 		fp = &fsys->next;
1658 	}
1659 	if(fsys == nil){
1660 		werrstr(EFsysNotFound, name);
1661 		wunlock(&sbox.lock);
1662 		return 0;
1663 	}
1664 	if(fsys->ref != 0 || fsys->fs != nil){
1665 		werrstr(EFsysBusy, fsys->name);
1666 		wunlock(&sbox.lock);
1667 		return 0;
1668 	}
1669 	*fp = fsys->next;
1670 	wunlock(&sbox.lock);
1671 
1672 	if(fsys->session != nil)
1673 		vtfreeconn(fsys->session);
1674 	if(fsys->venti != nil)
1675 		vtfree(fsys->venti);
1676 	if(fsys->dev != nil)
1677 		vtfree(fsys->dev);
1678 	if(fsys->name != nil)
1679 		vtfree(fsys->name);
1680 	vtfree(fsys);
1681 
1682 	return 1;
1683 }
1684 
1685 static int
fsysConfig(char * name,int argc,char * argv[])1686 fsysConfig(char* name, int argc, char* argv[])
1687 {
1688 	Fsys *fsys;
1689 	char *part;
1690 	char *usage = "usage: fsys name config [dev]";
1691 
1692 	ARGBEGIN{
1693 	default:
1694 		return cliError(usage);
1695 	}ARGEND
1696 	if(argc > 1)
1697 		return cliError(usage);
1698 
1699 	if(argc == 0)
1700 		part = foptname;
1701 	else
1702 		part = argv[0];
1703 
1704 	if((fsys = _fsysGet(part)) != nil){
1705 		qlock(&fsys->lock);
1706 		if(fsys->fs != nil){
1707 			werrstr(EFsysBusy, fsys->name);
1708 			qunlock(&fsys->lock);
1709 			fsysPut(fsys);
1710 			return 0;
1711 		}
1712 		vtfree(fsys->dev);
1713 		fsys->dev = vtstrdup(part);
1714 		qunlock(&fsys->lock);
1715 	}
1716 	else if((fsys = fsysAlloc(name, part)) == nil)
1717 		return 0;
1718 
1719 	fsysPut(fsys);
1720 	return 1;
1721 }
1722 
1723 static struct {
1724 	char*	cmd;
1725 	int	(*f)(Fsys*, int, char**);
1726 	int	(*f1)(char*, int, char**);
1727 } fsyscmd[] = {
1728 	{ "close",	fsysClose, },
1729 	{ "config",	nil, fsysConfig, },
1730 	{ "open",	nil, fsysOpen, },
1731 	{ "unconfig",	nil, fsysUnconfig, },
1732 	{ "venti",	nil, fsysVenti, },
1733 
1734 	{ "bfree",	fsysBfree, },
1735 	{ "block",	fsysBlock, },
1736 	{ "check",	fsysCheck, },
1737 	{ "clre",	fsysClre, },
1738 	{ "clri",	fsysClri, },
1739 	{ "clrp",	fsysClrp, },
1740 	{ "create",	fsysCreate, },
1741 	{ "df",		fsysDf, },
1742 	{ "epoch",	fsysEpoch, },
1743 	{ "halt",	fsysHalt, },
1744 	{ "label",	fsysLabel, },
1745 	{ "remove",	fsysRemove, },
1746 	{ "snap",	fsysSnap, },
1747 	{ "snaptime",	fsysSnapTime, },
1748 	{ "snapclean",	fsysSnapClean, },
1749 	{ "stat",	fsysStat, },
1750 	{ "sync",	fsysSync, },
1751 	{ "unhalt",	fsysUnhalt, },
1752 	{ "wstat",	fsysWstat, },
1753 	{ "vac",	fsysVac, },
1754 
1755 	{ nil,		nil, },
1756 };
1757 
1758 static int
fsysXXX1(Fsys * fsys,int i,int argc,char * argv[])1759 fsysXXX1(Fsys *fsys, int i, int argc, char* argv[])
1760 {
1761 	int r;
1762 
1763 	qlock(&fsys->lock);
1764 	if(fsys->fs == nil){
1765 		qunlock(&fsys->lock);
1766 		werrstr(EFsysNotOpen, fsys->name);
1767 		return 0;
1768 	}
1769 
1770 	if(fsys->fs->halted
1771 	&& fsyscmd[i].f != fsysUnhalt && fsyscmd[i].f != fsysCheck){
1772 		werrstr("file system %s is halted", fsys->name);
1773 		qunlock(&fsys->lock);
1774 		return 0;
1775 	}
1776 
1777 	r = (*fsyscmd[i].f)(fsys, argc, argv);
1778 	qunlock(&fsys->lock);
1779 	return r;
1780 }
1781 
1782 static int
fsysXXX(char * name,int argc,char * argv[])1783 fsysXXX(char* name, int argc, char* argv[])
1784 {
1785 	int i, r;
1786 	Fsys *fsys;
1787 
1788 	for(i = 0; fsyscmd[i].cmd != nil; i++){
1789 		if(strcmp(fsyscmd[i].cmd, argv[0]) == 0)
1790 			break;
1791 	}
1792 
1793 	if(fsyscmd[i].cmd == nil){
1794 		werrstr("unknown command - '%s'", argv[0]);
1795 		return 0;
1796 	}
1797 
1798 	/* some commands want the name... */
1799 	if(fsyscmd[i].f1 != nil){
1800 		if(strcmp(name, FsysAll) == 0){
1801 			werrstr("cannot use fsys %#q with %#q command", FsysAll, argv[0]);
1802 			return 0;
1803 		}
1804 		return (*fsyscmd[i].f1)(name, argc, argv);
1805 	}
1806 
1807 	/* ... but most commands want the Fsys */
1808 	if(strcmp(name, FsysAll) == 0){
1809 		r = 1;
1810 		rlock(&sbox.lock);
1811 		for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
1812 			fsys->ref++;
1813 			r = fsysXXX1(fsys, i, argc, argv) && r;
1814 			fsys->ref--;
1815 		}
1816 		runlock(&sbox.lock);
1817 	}else{
1818 		if((fsys = _fsysGet(name)) == nil)
1819 			return 0;
1820 		r = fsysXXX1(fsys, i, argc, argv);
1821 		fsysPut(fsys);
1822 	}
1823 	return r;
1824 }
1825 
1826 static int
cmdFsysXXX(int argc,char * argv[])1827 cmdFsysXXX(int argc, char* argv[])
1828 {
1829 	char *name;
1830 
1831 	if((name = sbox.curfsys) == nil){
1832 		werrstr(EFsysNoCurrent, argv[0]);
1833 		return 0;
1834 	}
1835 
1836 	return fsysXXX(name, argc, argv);
1837 }
1838 
1839 static int
cmdFsys(int argc,char * argv[])1840 cmdFsys(int argc, char* argv[])
1841 {
1842 	Fsys *fsys;
1843 	char *usage = "usage: fsys [name ...]";
1844 
1845 	ARGBEGIN{
1846 	default:
1847 		return cliError(usage);
1848 	}ARGEND
1849 
1850 	if(argc == 0){
1851 		rlock(&sbox.lock);
1852 		currfsysname = sbox.head->name;
1853 		for(fsys = sbox.head; fsys != nil; fsys = fsys->next)
1854 			consPrint("\t%s\n", fsys->name);
1855 		runlock(&sbox.lock);
1856 		return 1;
1857 	}
1858 	if(argc == 1){
1859 		fsys = nil;
1860 		if(strcmp(argv[0], FsysAll) != 0 && (fsys = fsysGet(argv[0])) == nil)
1861 			return 0;
1862 		sbox.curfsys = vtstrdup(argv[0]);
1863 		consPrompt(sbox.curfsys);
1864 		if(fsys)
1865 			fsysPut(fsys);
1866 		return 1;
1867 	}
1868 
1869 	return fsysXXX(argv[0], argc-1, argv+1);
1870 }
1871 
1872 int
fsysInit(void)1873 fsysInit(void)
1874 {
1875 	int i;
1876 
1877 	fmtinstall('H', encodefmt);
1878 	fmtinstall('V', scoreFmt);
1879 	fmtinstall('L', labelFmt);
1880 
1881 	cliAddCmd("fsys", cmdFsys);
1882 	for(i = 0; fsyscmd[i].cmd != nil; i++){
1883 		if(fsyscmd[i].f != nil)
1884 			cliAddCmd(fsyscmd[i].cmd, cmdFsysXXX);
1885 	}
1886 	/* the venti cmd is special: the fs can be either open or closed */
1887 	cliAddCmd("venti", cmdFsysXXX);
1888 	cliAddCmd("printconfig", cmdPrintConfig);
1889 
1890 	return 1;
1891 }
1892