plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

9fsys.c (35143B)


      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 *
     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
     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 *
     71 myDial(char *host)
     72 {
     73 	prventihost(host);
     74 	return vtdial(host);
     75 }
     76 
     77 static int
     78 myRedial(VtConn *z, char *host)
     79 {
     80 	prventihost(host);
     81 	return vtredial(z, host);
     82 }
     83 
     84 static Fsys*
     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
    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*
    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*
    151 fsysGetName(Fsys* fsys)
    152 {
    153 	return fsys->name;
    154 }
    155 
    156 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
    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*
    176 fsysGetFs(Fsys* fsys)
    177 {
    178 	assert(fsys != nil && fsys->fs != nil);
    179 
    180 	return fsys->fs;
    181 }
    182 
    183 void
    184 fsysFsRlock(Fsys* fsys)
    185 {
    186 	rlock(&fsys->fs->elk);
    187 }
    188 
    189 void
    190 fsysFsRUnlock(Fsys* fsys)
    191 {
    192 	runlock(&fsys->fs->elk);
    193 }
    194 
    195 int
    196 fsysNoAuthCheck(Fsys* fsys)
    197 {
    198 	return fsys->noauth;
    199 }
    200 
    201 int
    202 fsysNoPermCheck(Fsys* fsys)
    203 {
    204 	return fsys->noperm;
    205 }
    206 
    207 int
    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*
    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
    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*
    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*
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    924 fsysClre(Fsys* fsys, int argc, char* argv[])
    925 {
    926 	return fsysClrep(fsys, argc, argv, 'e');
    927 }
    928 
    929 static int
    930 fsysClrp(Fsys* fsys, int argc, char* argv[])
    931 {
    932 	return fsysClrep(fsys, argc, argv, 'p');
    933 }
    934 
    935 static int
    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
    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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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 }