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