aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/fossil/9fsys.c
diff options
context:
space:
mode:
authorDavid du Colombier <0intro@gmail.com>2013-09-23 23:00:39 +0200
committerDavid du Colombier <0intro@gmail.com>2013-09-23 23:00:39 +0200
commit6f4d00ee45693290fae042b27536b54f77b96acd (patch)
tree60ad31bf16ed2000661c02345dd2a63851588a5d /src/cmd/fossil/9fsys.c
parentfea86f063930ea187f1c77e93207ac8d39125520 (diff)
downloadplan9port-6f4d00ee45693290fae042b27536b54f77b96acd.tar.gz
plan9port-6f4d00ee45693290fae042b27536b54f77b96acd.tar.bz2
plan9port-6f4d00ee45693290fae042b27536b54f77b96acd.zip
fossil: import from plan 9
R=rsc https://codereview.appspot.com/7988047
Diffstat (limited to 'src/cmd/fossil/9fsys.c')
-rw-r--r--src/cmd/fossil/9fsys.c1896
1 files changed, 1896 insertions, 0 deletions
diff --git a/src/cmd/fossil/9fsys.c b/src/cmd/fossil/9fsys.c
new file mode 100644
index 00000000..4e139f70
--- /dev/null
+++ b/src/cmd/fossil/9fsys.c
@@ -0,0 +1,1896 @@
+#include "stdinc.h"
+#include <bio.h>
+#include "dat.h"
+#include "fns.h"
+#include "9.h"
+
+struct Fsys {
+ VtLock* lock;
+
+ char* name; /* copy here & Fs to ease error reporting */
+ char* dev;
+ char* venti;
+
+ Fs* fs;
+ VtSession* session;
+ int ref;
+
+ int noauth;
+ int noperm;
+ int wstatallow;
+
+ Fsys* next;
+};
+
+int mempcnt; /* from fossil.c */
+
+int fsGetBlockSize(Fs *fs);
+
+static struct {
+ VtLock* lock;
+ Fsys* head;
+ Fsys* tail;
+
+ char* curfsys;
+} sbox;
+
+static char *_argv0;
+#define argv0 _argv0
+
+static char FsysAll[] = "all";
+
+static char EFsysBusy[] = "fsys: '%s' busy";
+static char EFsysExists[] = "fsys: '%s' already exists";
+static char EFsysNoCurrent[] = "fsys: no current fsys";
+static char EFsysNotFound[] = "fsys: '%s' not found";
+static char EFsysNotOpen[] = "fsys: '%s' not open";
+
+static char *
+ventihost(char *host)
+{
+ if(host != nil)
+ return vtStrDup(host);
+ host = getenv("venti");
+ if(host == nil)
+ host = vtStrDup("$venti");
+ return host;
+}
+
+static void
+prventihost(char *host)
+{
+ char *vh;
+
+ vh = ventihost(host);
+ fprint(2, "%s: dialing venti at %s\n",
+ argv0, netmkaddr(vh, 0, "venti"));
+ free(vh);
+}
+
+static VtSession *
+myDial(char *host, int canfail)
+{
+ prventihost(host);
+ return vtDial(host, canfail);
+}
+
+static int
+myRedial(VtSession *z, char *host)
+{
+ prventihost(host);
+ return vtRedial(z, host);
+}
+
+static Fsys*
+_fsysGet(char* name)
+{
+ Fsys *fsys;
+
+ if(name == nil || name[0] == '\0')
+ name = "main";
+
+ vtRLock(sbox.lock);
+ for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
+ if(strcmp(name, fsys->name) == 0){
+ fsys->ref++;
+ break;
+ }
+ }
+ vtRUnlock(sbox.lock);
+ if(fsys == nil)
+ vtSetError(EFsysNotFound, name);
+ return fsys;
+}
+
+static int
+cmdPrintConfig(int argc, char* argv[])
+{
+ Fsys *fsys;
+ char *usage = "usage: printconfig";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+
+ if(argc)
+ return cliError(usage);
+
+ vtRLock(sbox.lock);
+ for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
+ consPrint("\tfsys %s config %s\n", fsys->name, fsys->dev);
+ if(fsys->venti && fsys->venti[0])
+ consPrint("\tfsys %s venti %q\n", fsys->name,
+ fsys->venti);
+ }
+ vtRUnlock(sbox.lock);
+ return 1;
+}
+
+Fsys*
+fsysGet(char* name)
+{
+ Fsys *fsys;
+
+ if((fsys = _fsysGet(name)) == nil)
+ return nil;
+
+ vtLock(fsys->lock);
+ if(fsys->fs == nil){
+ vtSetError(EFsysNotOpen, fsys->name);
+ vtUnlock(fsys->lock);
+ fsysPut(fsys);
+ return nil;
+ }
+ vtUnlock(fsys->lock);
+
+ return fsys;
+}
+
+char*
+fsysGetName(Fsys* fsys)
+{
+ return fsys->name;
+}
+
+Fsys*
+fsysIncRef(Fsys* fsys)
+{
+ vtLock(sbox.lock);
+ fsys->ref++;
+ vtUnlock(sbox.lock);
+
+ return fsys;
+}
+
+void
+fsysPut(Fsys* fsys)
+{
+ vtLock(sbox.lock);
+ assert(fsys->ref > 0);
+ fsys->ref--;
+ vtUnlock(sbox.lock);
+}
+
+Fs*
+fsysGetFs(Fsys* fsys)
+{
+ assert(fsys != nil && fsys->fs != nil);
+
+ return fsys->fs;
+}
+
+void
+fsysFsRlock(Fsys* fsys)
+{
+ vtRLock(fsys->fs->elk);
+}
+
+void
+fsysFsRUnlock(Fsys* fsys)
+{
+ vtRUnlock(fsys->fs->elk);
+}
+
+int
+fsysNoAuthCheck(Fsys* fsys)
+{
+ return fsys->noauth;
+}
+
+int
+fsysNoPermCheck(Fsys* fsys)
+{
+ return fsys->noperm;
+}
+
+int
+fsysWstatAllow(Fsys* fsys)
+{
+ return fsys->wstatallow;
+}
+
+static char modechars[] = "YUGalLdHSATs";
+static ulong modebits[] = {
+ ModeSticky,
+ ModeSetUid,
+ ModeSetGid,
+ ModeAppend,
+ ModeExclusive,
+ ModeLink,
+ ModeDir,
+ ModeHidden,
+ ModeSystem,
+ ModeArchive,
+ ModeTemporary,
+ ModeSnapshot,
+ 0
+};
+
+char*
+fsysModeString(ulong mode, char *buf)
+{
+ int i;
+ char *p;
+
+ p = buf;
+ for(i=0; modebits[i]; i++)
+ if(mode & modebits[i])
+ *p++ = modechars[i];
+ sprint(p, "%luo", mode&0777);
+ return buf;
+}
+
+int
+fsysParseMode(char* s, ulong* mode)
+{
+ ulong x, y;
+ char *p;
+
+ x = 0;
+ for(; *s < '0' || *s > '9'; s++){
+ if(*s == 0)
+ return 0;
+ p = strchr(modechars, *s);
+ if(p == nil)
+ return 0;
+ x |= modebits[p-modechars];
+ }
+ y = strtoul(s, &p, 8);
+ if(*p != '\0' || y > 0777)
+ return 0;
+ *mode = x|y;
+ return 1;
+}
+
+File*
+fsysGetRoot(Fsys* fsys, char* name)
+{
+ File *root, *sub;
+
+ assert(fsys != nil && fsys->fs != nil);
+
+ root = fsGetRoot(fsys->fs);
+ if(name == nil || strcmp(name, "") == 0)
+ return root;
+
+ sub = fileWalk(root, name);
+ fileDecRef(root);
+
+ return sub;
+}
+
+static Fsys*
+fsysAlloc(char* name, char* dev)
+{
+ Fsys *fsys;
+
+ vtLock(sbox.lock);
+ for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
+ if(strcmp(fsys->name, name) != 0)
+ continue;
+ vtSetError(EFsysExists, name);
+ vtUnlock(sbox.lock);
+ return nil;
+ }
+
+ fsys = vtMemAllocZ(sizeof(Fsys));
+ fsys->lock = vtLockAlloc();
+ fsys->name = vtStrDup(name);
+ fsys->dev = vtStrDup(dev);
+
+ fsys->ref = 1;
+
+ if(sbox.tail != nil)
+ sbox.tail->next = fsys;
+ else
+ sbox.head = fsys;
+ sbox.tail = fsys;
+ vtUnlock(sbox.lock);
+
+ return fsys;
+}
+
+static int
+fsysClose(Fsys* fsys, int argc, char* argv[])
+{
+ char *usage = "usage: [fsys name] close";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc)
+ return cliError(usage);
+
+ return cliError("close isn't working yet; halt %s and then kill fossil",
+ fsys->name);
+
+ /*
+ * Oooh. This could be hard. What if fsys->ref != 1?
+ * Also, fsClose() either does the job or panics, can we
+ * gracefully detect it's still busy?
+ *
+ * More thought and care needed here.
+ fsClose(fsys->fs);
+ fsys->fs = nil;
+ vtClose(fsys->session);
+ fsys->session = nil;
+
+ if(sbox.curfsys != nil && strcmp(fsys->name, sbox.curfsys) == 0){
+ sbox.curfsys = nil;
+ consPrompt(nil);
+ }
+
+ return 1;
+ */
+}
+
+static int
+fsysVac(Fsys* fsys, int argc, char* argv[])
+{
+ uchar score[VtScoreSize];
+ char *usage = "usage: [fsys name] vac path";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc != 1)
+ return cliError(usage);
+
+ if(!fsVac(fsys->fs, argv[0], score))
+ return 0;
+
+ consPrint("vac:%V\n", score);
+ return 1;
+}
+
+static int
+fsysSnap(Fsys* fsys, int argc, char* argv[])
+{
+ int doarchive;
+ char *usage = "usage: [fsys name] snap [-a] [-s /active] [-d /archive/yyyy/mmmm]";
+ char *src, *dst;
+
+ src = nil;
+ dst = nil;
+ doarchive = 0;
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ case 'a':
+ doarchive = 1;
+ break;
+ case 'd':
+ if((dst = ARGF()) == nil)
+ return cliError(usage);
+ break;
+ case 's':
+ if((src = ARGF()) == nil)
+ return cliError(usage);
+ break;
+ }ARGEND
+ if(argc)
+ return cliError(usage);
+
+ if(!fsSnapshot(fsys->fs, src, dst, doarchive))
+ return 0;
+
+ return 1;
+}
+
+static int
+fsysSnapClean(Fsys *fsys, int argc, char* argv[])
+{
+ u32int arch, snap, life;
+ char *usage = "usage: [fsys name] snapclean [maxminutes]\n";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+
+ if(argc > 1)
+ return cliError(usage);
+ if(argc == 1)
+ life = atoi(argv[0]);
+ else
+ snapGetTimes(fsys->fs->snap, &arch, &snap, &life);
+
+ fsSnapshotCleanup(fsys->fs, life);
+ return 1;
+}
+
+static int
+fsysSnapTime(Fsys* fsys, int argc, char* argv[])
+{
+ char buf[128], *x;
+ int hh, mm, changed;
+ u32int arch, snap, life;
+ char *usage = "usage: [fsys name] snaptime [-a hhmm] [-s snapminutes] [-t maxminutes]";
+
+ changed = 0;
+ snapGetTimes(fsys->fs->snap, &arch, &snap, &life);
+ ARGBEGIN{
+ case 'a':
+ changed = 1;
+ x = ARGF();
+ if(x == nil)
+ return cliError(usage);
+ if(strcmp(x, "none") == 0){
+ arch = ~(u32int)0;
+ break;
+ }
+ if(strlen(x) != 4 || strspn(x, "0123456789") != 4)
+ return cliError(usage);
+ hh = (x[0]-'0')*10 + x[1]-'0';
+ mm = (x[2]-'0')*10 + x[3]-'0';
+ if(hh >= 24 || mm >= 60)
+ return cliError(usage);
+ arch = hh*60+mm;
+ break;
+ case 's':
+ changed = 1;
+ x = ARGF();
+ if(x == nil)
+ return cliError(usage);
+ if(strcmp(x, "none") == 0){
+ snap = ~(u32int)0;
+ break;
+ }
+ snap = atoi(x);
+ break;
+ case 't':
+ changed = 1;
+ x = ARGF();
+ if(x == nil)
+ return cliError(usage);
+ if(strcmp(x, "none") == 0){
+ life = ~(u32int)0;
+ break;
+ }
+ life = atoi(x);
+ break;
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc > 0)
+ return cliError(usage);
+
+ if(changed){
+ snapSetTimes(fsys->fs->snap, arch, snap, life);
+ return 1;
+ }
+ snapGetTimes(fsys->fs->snap, &arch, &snap, &life);
+ if(arch != ~(u32int)0)
+ sprint(buf, "-a %02d%02d", arch/60, arch%60);
+ else
+ sprint(buf, "-a none");
+ if(snap != ~(u32int)0)
+ sprint(buf+strlen(buf), " -s %d", snap);
+ else
+ sprint(buf+strlen(buf), " -s none");
+ if(life != ~(u32int)0)
+ sprint(buf+strlen(buf), " -t %ud", life);
+ else
+ sprint(buf+strlen(buf), " -t none");
+ consPrint("\tsnaptime %s\n", buf);
+ return 1;
+}
+
+static int
+fsysSync(Fsys* fsys, int argc, char* argv[])
+{
+ char *usage = "usage: [fsys name] sync";
+ int n;
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc > 0)
+ return cliError(usage);
+
+ n = cacheDirty(fsys->fs->cache);
+ fsSync(fsys->fs);
+ consPrint("\t%s sync: wrote %d blocks\n", fsys->name, n);
+ return 1;
+}
+
+static int
+fsysHalt(Fsys *fsys, int argc, char* argv[])
+{
+ char *usage = "usage: [fsys name] halt";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc > 0)
+ return cliError(usage);
+
+ fsHalt(fsys->fs);
+ return 1;
+}
+
+static int
+fsysUnhalt(Fsys *fsys, int argc, char* argv[])
+{
+ char *usage = "usage: [fsys name] unhalt";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc > 0)
+ return cliError(usage);
+
+ if(!fsys->fs->halted)
+ return cliError("file system %s not halted", fsys->name);
+
+ fsUnhalt(fsys->fs);
+ return 1;
+}
+
+static int
+fsysRemove(Fsys* fsys, int argc, char* argv[])
+{
+ File *file;
+ char *usage = "usage: [fsys name] remove path ...";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc == 0)
+ return cliError(usage);
+
+ vtRLock(fsys->fs->elk);
+ while(argc > 0){
+ if((file = fileOpen(fsys->fs, argv[0])) == nil)
+ consPrint("%s: %R\n", argv[0]);
+ else{
+ if(!fileRemove(file, uidadm))
+ consPrint("%s: %R\n", argv[0]);
+ fileDecRef(file);
+ }
+ argc--;
+ argv++;
+ }
+ vtRUnlock(fsys->fs->elk);
+
+ return 1;
+}
+
+static int
+fsysClri(Fsys* fsys, int argc, char* argv[])
+{
+ char *usage = "usage: [fsys name] clri path ...";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc == 0)
+ return cliError(usage);
+
+ vtRLock(fsys->fs->elk);
+ while(argc > 0){
+ if(!fileClriPath(fsys->fs, argv[0], uidadm))
+ consPrint("clri %s: %R\n", argv[0]);
+ argc--;
+ argv++;
+ }
+ vtRUnlock(fsys->fs->elk);
+
+ return 1;
+}
+
+/*
+ * Inspect and edit the labels for blocks on disk.
+ */
+static int
+fsysLabel(Fsys* fsys, int argc, char* argv[])
+{
+ Fs *fs;
+ Label l;
+ int n, r;
+ u32int addr;
+ Block *b, *bb;
+ char *usage = "usage: [fsys name] label addr [type state epoch epochClose tag]";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc != 1 && argc != 6)
+ return cliError(usage);
+
+ r = 0;
+ vtRLock(fsys->fs->elk);
+
+ fs = fsys->fs;
+ addr = strtoul(argv[0], 0, 0);
+ b = cacheLocal(fs->cache, PartData, addr, OReadOnly);
+ if(b == nil)
+ goto Out0;
+
+ l = b->l;
+ consPrint("%slabel %#ux %ud %ud %ud %ud %#x\n",
+ argc==6 ? "old: " : "", addr, l.type, l.state,
+ l.epoch, l.epochClose, l.tag);
+
+ if(argc == 6){
+ if(strcmp(argv[1], "-") != 0)
+ l.type = atoi(argv[1]);
+ if(strcmp(argv[2], "-") != 0)
+ l.state = atoi(argv[2]);
+ if(strcmp(argv[3], "-") != 0)
+ l.epoch = strtoul(argv[3], 0, 0);
+ if(strcmp(argv[4], "-") != 0)
+ l.epochClose = strtoul(argv[4], 0, 0);
+ if(strcmp(argv[5], "-") != 0)
+ l.tag = strtoul(argv[5], 0, 0);
+
+ consPrint("new: label %#ux %ud %ud %ud %ud %#x\n",
+ addr, l.type, l.state, l.epoch, l.epochClose, l.tag);
+ bb = _blockSetLabel(b, &l);
+ if(bb == nil)
+ goto Out1;
+ n = 0;
+ for(;;){
+ if(blockWrite(bb, Waitlock)){
+ while(bb->iostate != BioClean){
+ assert(bb->iostate == BioWriting);
+ vtSleep(bb->ioready);
+ }
+ break;
+ }
+ consPrint("blockWrite: %R\n");
+ if(n++ >= 5){
+ consPrint("giving up\n");
+ break;
+ }
+ sleep(5*1000);
+ }
+ blockPut(bb);
+ }
+ r = 1;
+Out1:
+ blockPut(b);
+Out0:
+ vtRUnlock(fs->elk);
+
+ return r;
+}
+
+/*
+ * Inspect and edit the blocks on disk.
+ */
+static int
+fsysBlock(Fsys* fsys, int argc, char* argv[])
+{
+ Fs *fs;
+ char *s;
+ Block *b;
+ uchar *buf;
+ u32int addr;
+ int c, count, i, offset;
+ char *usage = "usage: [fsys name] block addr offset [count [data]]";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc < 2 || argc > 4)
+ return cliError(usage);
+
+ fs = fsys->fs;
+ addr = strtoul(argv[0], 0, 0);
+ offset = strtoul(argv[1], 0, 0);
+ if(offset < 0 || offset >= fs->blockSize){
+ vtSetError("bad offset");
+ return 0;
+ }
+ if(argc > 2)
+ count = strtoul(argv[2], 0, 0);
+ else
+ count = 100000000;
+ if(offset+count > fs->blockSize)
+ count = fs->blockSize - count;
+
+ vtRLock(fs->elk);
+
+ b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly);
+ if(b == nil){
+ vtSetError("cacheLocal %#ux: %R", addr);
+ vtRUnlock(fs->elk);
+ return 0;
+ }
+
+ consPrint("\t%sblock %#ux %ud %ud %.*H\n",
+ argc==4 ? "old: " : "", addr, offset, count, count, b->data+offset);
+
+ if(argc == 4){
+ s = argv[3];
+ if(strlen(s) != 2*count){
+ vtSetError("bad data count");
+ goto Out;
+ }
+ buf = vtMemAllocZ(count);
+ for(i = 0; i < count*2; i++){
+ if(s[i] >= '0' && s[i] <= '9')
+ c = s[i] - '0';
+ else if(s[i] >= 'a' && s[i] <= 'f')
+ c = s[i] - 'a' + 10;
+ else if(s[i] >= 'A' && s[i] <= 'F')
+ c = s[i] - 'A' + 10;
+ else{
+ vtSetError("bad hex");
+ vtMemFree(buf);
+ goto Out;
+ }
+ if((i & 1) == 0)
+ c <<= 4;
+ buf[i>>1] |= c;
+ }
+ memmove(b->data+offset, buf, count);
+ consPrint("\tnew: block %#ux %ud %ud %.*H\n",
+ addr, offset, count, count, b->data+offset);
+ blockDirty(b);
+ }
+
+Out:
+ blockPut(b);
+ vtRUnlock(fs->elk);
+
+ return 1;
+}
+
+/*
+ * Free a disk block.
+ */
+static int
+fsysBfree(Fsys* fsys, int argc, char* argv[])
+{
+ Fs *fs;
+ Label l;
+ char *p;
+ Block *b;
+ u32int addr;
+ char *usage = "usage: [fsys name] bfree addr ...";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc == 0)
+ return cliError(usage);
+
+ fs = fsys->fs;
+ vtRLock(fs->elk);
+ while(argc > 0){
+ addr = strtoul(argv[0], &p, 0);
+ if(*p != '\0'){
+ consPrint("bad address - '%ud'\n", addr);
+ /* syntax error; let's stop */
+ vtRUnlock(fs->elk);
+ return 0;
+ }
+ b = cacheLocal(fs->cache, PartData, addr, OReadOnly);
+ if(b == nil){
+ consPrint("loading %#ux: %R\n", addr);
+ continue;
+ }
+ l = b->l;
+ if(l.state == BsFree)
+ consPrint("%#ux is already free\n", addr);
+ else{
+ consPrint("label %#ux %ud %ud %ud %ud %#x\n",
+ addr, l.type, l.state, l.epoch, l.epochClose, l.tag);
+ l.state = BsFree;
+ l.type = BtMax;
+ l.tag = 0;
+ l.epoch = 0;
+ l.epochClose = 0;
+ if(!blockSetLabel(b, &l, 0))
+ consPrint("freeing %#ux: %R\n", addr);
+ }
+ blockPut(b);
+ argc--;
+ argv++;
+ }
+ vtRUnlock(fs->elk);
+
+ return 1;
+}
+
+static int
+fsysDf(Fsys *fsys, int argc, char* argv[])
+{
+ char *usage = "usage: [fsys name] df";
+ u32int used, tot, bsize;
+ Fs *fs;
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc != 0)
+ return cliError(usage);
+
+ fs = fsys->fs;
+ cacheCountUsed(fs->cache, fs->elo, &used, &tot, &bsize);
+ consPrint("\t%s: %,llud used + %,llud free = %,llud (%.1f%% used)\n",
+ fsys->name, used*(vlong)bsize, (tot-used)*(vlong)bsize,
+ tot*(vlong)bsize, used*100.0/tot);
+ return 1;
+}
+
+/*
+ * Zero an entry or a pointer.
+ */
+static int
+fsysClrep(Fsys* fsys, int argc, char* argv[], int ch)
+{
+ Fs *fs;
+ Entry e;
+ Block *b;
+ u32int addr;
+ int i, max, offset, sz;
+ uchar zero[VtEntrySize];
+ char *usage = "usage: [fsys name] clr%c addr offset ...";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage, ch);
+ }ARGEND
+ if(argc < 2)
+ return cliError(usage, ch);
+
+ fs = fsys->fs;
+ vtRLock(fsys->fs->elk);
+
+ addr = strtoul(argv[0], 0, 0);
+ b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly);
+ if(b == nil){
+ vtSetError("cacheLocal %#ux: %R", addr);
+ Err:
+ vtRUnlock(fsys->fs->elk);
+ return 0;
+ }
+
+ switch(ch){
+ default:
+ vtSetError("clrep");
+ goto Err;
+ case 'e':
+ if(b->l.type != BtDir){
+ vtSetError("wrong block type");
+ goto Err;
+ }
+ sz = VtEntrySize;
+ memset(&e, 0, sizeof e);
+ entryPack(&e, zero, 0);
+ break;
+ case 'p':
+ if(b->l.type == BtDir || b->l.type == BtData){
+ vtSetError("wrong block type");
+ goto Err;
+ }
+ sz = VtScoreSize;
+ memmove(zero, vtZeroScore, VtScoreSize);
+ break;
+ }
+ max = fs->blockSize/sz;
+
+ for(i = 1; i < argc; i++){
+ offset = atoi(argv[i]);
+ if(offset >= max){
+ consPrint("\toffset %d too large (>= %d)\n", i, max);
+ continue;
+ }
+ consPrint("\tblock %#ux %d %d %.*H\n", addr, offset*sz, sz, sz, b->data+offset*sz);
+ memmove(b->data+offset*sz, zero, sz);
+ }
+ blockDirty(b);
+ blockPut(b);
+ vtRUnlock(fsys->fs->elk);
+
+ return 1;
+}
+
+static int
+fsysClre(Fsys* fsys, int argc, char* argv[])
+{
+ return fsysClrep(fsys, argc, argv, 'e');
+}
+
+static int
+fsysClrp(Fsys* fsys, int argc, char* argv[])
+{
+ return fsysClrep(fsys, argc, argv, 'p');
+}
+
+static int
+fsysEsearch1(File* f, char* s, u32int elo)
+{
+ int n, r;
+ DirEntry de;
+ DirEntryEnum *dee;
+ File *ff;
+ Entry e, ee;
+ char *t;
+
+ dee = deeOpen(f);
+ if(dee == nil)
+ return 0;
+
+ n = 0;
+ for(;;){
+ r = deeRead(dee, &de);
+ if(r < 0){
+ consPrint("\tdeeRead %s/%s: %R\n", s, de.elem);
+ break;
+ }
+ if(r == 0)
+ break;
+ if(de.mode & ModeSnapshot){
+ if((ff = fileWalk(f, de.elem)) == nil)
+ consPrint("\tcannot walk %s/%s: %R\n", s, de.elem);
+ else{
+ if(!fileGetSources(ff, &e, &ee))
+ consPrint("\tcannot get sources for %s/%s: %R\n", s, de.elem);
+ else if(e.snap != 0 && e.snap < elo){
+ consPrint("\t%ud\tclri %s/%s\n", e.snap, s, de.elem);
+ n++;
+ }
+ fileDecRef(ff);
+ }
+ }
+ else if(de.mode & ModeDir){
+ if((ff = fileWalk(f, de.elem)) == nil)
+ consPrint("\tcannot walk %s/%s: %R\n", s, de.elem);
+ else{
+ t = smprint("%s/%s", s, de.elem);
+ n += fsysEsearch1(ff, t, elo);
+ vtMemFree(t);
+ fileDecRef(ff);
+ }
+ }
+ deCleanup(&de);
+ if(r < 0)
+ break;
+ }
+ deeClose(dee);
+
+ return n;
+}
+
+static int
+fsysEsearch(Fs* fs, char* path, u32int elo)
+{
+ int n;
+ File *f;
+ DirEntry de;
+
+ f = fileOpen(fs, path);
+ if(f == nil)
+ return 0;
+ if(!fileGetDir(f, &de)){
+ consPrint("\tfileGetDir %s failed: %R\n", path);
+ fileDecRef(f);
+ return 0;
+ }
+ if((de.mode & ModeDir) == 0){
+ fileDecRef(f);
+ deCleanup(&de);
+ return 0;
+ }
+ deCleanup(&de);
+ n = fsysEsearch1(f, path, elo);
+ fileDecRef(f);
+ return n;
+}
+
+static int
+fsysEpoch(Fsys* fsys, int argc, char* argv[])
+{
+ Fs *fs;
+ int force, n, remove;
+ u32int low, old;
+ char *usage = "usage: [fsys name] epoch [[-ry] low]";
+
+ force = 0;
+ remove = 0;
+ ARGBEGIN{
+ case 'y':
+ force = 1;
+ break;
+ case 'r':
+ remove = 1;
+ break;
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc > 1)
+ return cliError(usage);
+ if(argc > 0)
+ low = strtoul(argv[0], 0, 0);
+ else
+ low = ~(u32int)0;
+
+ if(low == 0)
+ return cliError("low epoch cannot be zero");
+
+ fs = fsys->fs;
+
+ vtRLock(fs->elk);
+ consPrint("\tlow %ud hi %ud\n", fs->elo, fs->ehi);
+ if(low == ~(u32int)0){
+ vtRUnlock(fs->elk);
+ return 1;
+ }
+ n = fsysEsearch(fsys->fs, "/archive", low);
+ n += fsysEsearch(fsys->fs, "/snapshot", low);
+ consPrint("\t%d snapshot%s found with epoch < %ud\n", n, n==1 ? "" : "s", low);
+ vtRUnlock(fs->elk);
+
+ /*
+ * There's a small race here -- a new snapshot with epoch < low might
+ * get introduced now that we unlocked fs->elk. Low has to
+ * be <= fs->ehi. Of course, in order for this to happen low has
+ * to be equal to the current fs->ehi _and_ a snapshot has to
+ * run right now. This is a small enough window that I don't care.
+ */
+ if(n != 0 && !force){
+ consPrint("\tnot setting low epoch\n");
+ return 1;
+ }
+ old = fs->elo;
+ if(!fsEpochLow(fs, low))
+ consPrint("\tfsEpochLow: %R\n");
+ else{
+ consPrint("\told: epoch%s %ud\n", force ? " -y" : "", old);
+ consPrint("\tnew: epoch%s %ud\n", force ? " -y" : "", fs->elo);
+ if(fs->elo < low)
+ consPrint("\twarning: new low epoch < old low epoch\n");
+ if(force && remove)
+ fsSnapshotRemove(fs);
+ }
+
+ return 1;
+}
+
+static int
+fsysCreate(Fsys* fsys, int argc, char* argv[])
+{
+ int r;
+ ulong mode;
+ char *elem, *p, *path;
+ char *usage = "usage: [fsys name] create path uid gid perm";
+ DirEntry de;
+ File *file, *parent;
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc != 4)
+ return cliError(usage);
+
+ if(!fsysParseMode(argv[3], &mode))
+ return cliError(usage);
+ if(mode&ModeSnapshot)
+ return cliError("create - cannot create with snapshot bit set");
+
+ if(strcmp(argv[1], uidnoworld) == 0)
+ return cliError("permission denied");
+
+ vtRLock(fsys->fs->elk);
+ path = vtStrDup(argv[0]);
+ if((p = strrchr(path, '/')) != nil){
+ *p++ = '\0';
+ elem = p;
+ p = path;
+ if(*p == '\0')
+ p = "/";
+ }
+ else{
+ p = "/";
+ elem = path;
+ }
+
+ r = 0;
+ if((parent = fileOpen(fsys->fs, p)) == nil)
+ goto out;
+
+ file = fileCreate(parent, elem, mode, argv[1]);
+ fileDecRef(parent);
+ if(file == nil){
+ vtSetError("create %s/%s: %R", p, elem);
+ goto out;
+ }
+
+ if(!fileGetDir(file, &de)){
+ vtSetError("stat failed after create: %R");
+ goto out1;
+ }
+
+ if(strcmp(de.gid, argv[2]) != 0){
+ vtMemFree(de.gid);
+ de.gid = vtStrDup(argv[2]);
+ if(!fileSetDir(file, &de, argv[1])){
+ vtSetError("wstat failed after create: %R");
+ goto out2;
+ }
+ }
+ r = 1;
+
+out2:
+ deCleanup(&de);
+out1:
+ fileDecRef(file);
+out:
+ vtMemFree(path);
+ vtRUnlock(fsys->fs->elk);
+
+ return r;
+}
+
+static void
+fsysPrintStat(char *prefix, char *file, DirEntry *de)
+{
+ char buf[64];
+
+ if(prefix == nil)
+ prefix = "";
+ consPrint("%sstat %q %q %q %q %s %llud\n", prefix,
+ file, de->elem, de->uid, de->gid, fsysModeString(de->mode, buf), de->size);
+}
+
+static int
+fsysStat(Fsys* fsys, int argc, char* argv[])
+{
+ int i;
+ File *f;
+ DirEntry de;
+ char *usage = "usage: [fsys name] stat files...";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+
+ if(argc == 0)
+ return cliError(usage);
+
+ vtRLock(fsys->fs->elk);
+ for(i=0; i<argc; i++){
+ if((f = fileOpen(fsys->fs, argv[i])) == nil){
+ consPrint("%s: %R\n", argv[i]);
+ continue;
+ }
+ if(!fileGetDir(f, &de)){
+ consPrint("%s: %R\n", argv[i]);
+ fileDecRef(f);
+ continue;
+ }
+ fsysPrintStat("\t", argv[i], &de);
+ deCleanup(&de);
+ fileDecRef(f);
+ }
+ vtRUnlock(fsys->fs->elk);
+ return 1;
+}
+
+static int
+fsysWstat(Fsys *fsys, int argc, char* argv[])
+{
+ File *f;
+ char *p;
+ DirEntry de;
+ char *usage = "usage: [fsys name] wstat file elem uid gid mode length\n"
+ "\tuse - for any field to mean don't change";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+
+ if(argc != 6)
+ return cliError(usage);
+
+ vtRLock(fsys->fs->elk);
+ if((f = fileOpen(fsys->fs, argv[0])) == nil){
+ vtSetError("console wstat - walk - %R");
+ vtRUnlock(fsys->fs->elk);
+ return 0;
+ }
+ if(!fileGetDir(f, &de)){
+ vtSetError("console wstat - stat - %R");
+ fileDecRef(f);
+ vtRUnlock(fsys->fs->elk);
+ return 0;
+ }
+ fsysPrintStat("\told: w", argv[0], &de);
+
+ if(strcmp(argv[1], "-") != 0){
+ if(!validFileName(argv[1])){
+ vtSetError("console wstat - bad elem");
+ goto error;
+ }
+ vtMemFree(de.elem);
+ de.elem = vtStrDup(argv[1]);
+ }
+ if(strcmp(argv[2], "-") != 0){
+ if(!validUserName(argv[2])){
+ vtSetError("console wstat - bad uid");
+ goto error;
+ }
+ vtMemFree(de.uid);
+ de.uid = vtStrDup(argv[2]);
+ }
+ if(strcmp(argv[3], "-") != 0){
+ if(!validUserName(argv[3])){
+ vtSetError("console wstat - bad gid");
+ goto error;
+ }
+ vtMemFree(de.gid);
+ de.gid = vtStrDup(argv[3]);
+ }
+ if(strcmp(argv[4], "-") != 0){
+ if(!fsysParseMode(argv[4], &de.mode)){
+ vtSetError("console wstat - bad mode");
+ goto error;
+ }
+ }
+ if(strcmp(argv[5], "-") != 0){
+ de.size = strtoull(argv[5], &p, 0);
+ if(argv[5][0] == '\0' || *p != '\0' || (vlong)de.size < 0){
+ vtSetError("console wstat - bad length");
+ goto error;
+ }
+ }
+
+ if(!fileSetDir(f, &de, uidadm)){
+ vtSetError("console wstat - %R");
+ goto error;
+ }
+ deCleanup(&de);
+
+ if(!fileGetDir(f, &de)){
+ vtSetError("console wstat - stat2 - %R");
+ goto error;
+ }
+ fsysPrintStat("\tnew: w", argv[0], &de);
+ deCleanup(&de);
+ fileDecRef(f);
+ vtRUnlock(fsys->fs->elk);
+
+ return 1;
+
+error:
+ deCleanup(&de); /* okay to do this twice */
+ fileDecRef(f);
+ vtRUnlock(fsys->fs->elk);
+ return 0;
+}
+
+static void
+fsckClri(Fsck *fsck, char *name, MetaBlock *mb, int i, Block *b)
+{
+ USED(name);
+
+ if((fsck->flags&DoClri) == 0)
+ return;
+
+ mbDelete(mb, i);
+ mbPack(mb);
+ blockDirty(b);
+}
+
+static void
+fsckClose(Fsck *fsck, Block *b, u32int epoch)
+{
+ Label l;
+
+ if((fsck->flags&DoClose) == 0)
+ return;
+ l = b->l;
+ if(l.state == BsFree || (l.state&BsClosed)){
+ consPrint("%#ux is already closed\n", b->addr);
+ return;
+ }
+ if(epoch){
+ l.state |= BsClosed;
+ l.epochClose = epoch;
+ }else
+ l.state = BsFree;
+
+ if(!blockSetLabel(b, &l, 0))
+ consPrint("%#ux setlabel: %R\n", b->addr);
+}
+
+static void
+fsckClre(Fsck *fsck, Block *b, int offset)
+{
+ Entry e;
+
+ if((fsck->flags&DoClre) == 0)
+ return;
+ if(offset<0 || offset*VtEntrySize >= fsck->bsize){
+ consPrint("bad clre\n");
+ return;
+ }
+ memset(&e, 0, sizeof e);
+ entryPack(&e, b->data, offset);
+ blockDirty(b);
+}
+
+static void
+fsckClrp(Fsck *fsck, Block *b, int offset)
+{
+ if((fsck->flags&DoClrp) == 0)
+ return;
+ if(offset<0 || offset*VtScoreSize >= fsck->bsize){
+ consPrint("bad clre\n");
+ return;
+ }
+ memmove(b->data+offset*VtScoreSize, vtZeroScore, VtScoreSize);
+ blockDirty(b);
+}
+
+static int
+fsysCheck(Fsys *fsys, int argc, char *argv[])
+{
+ int i, halting;
+ char *usage = "usage: [fsys name] check [-v] [options]";
+ Fsck fsck;
+ Block *b;
+ Super super;
+
+ memset(&fsck, 0, sizeof fsck);
+ fsck.fs = fsys->fs;
+ fsck.clri = fsckClri;
+ fsck.clre = fsckClre;
+ fsck.clrp = fsckClrp;
+ fsck.close = fsckClose;
+ fsck.print = consPrint;
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+
+ for(i=0; i<argc; i++){
+ if(strcmp(argv[i], "pblock") == 0)
+ fsck.printblocks = 1;
+ else if(strcmp(argv[i], "pdir") == 0)
+ fsck.printdirs = 1;
+ else if(strcmp(argv[i], "pfile") == 0)
+ fsck.printfiles = 1;
+ else if(strcmp(argv[i], "bclose") == 0)
+ fsck.flags |= DoClose;
+ else if(strcmp(argv[i], "clri") == 0)
+ fsck.flags |= DoClri;
+ else if(strcmp(argv[i], "clre") == 0)
+ fsck.flags |= DoClre;
+ else if(strcmp(argv[i], "clrp") == 0)
+ fsck.flags |= DoClrp;
+ else if(strcmp(argv[i], "fix") == 0)
+ fsck.flags |= DoClose|DoClri|DoClre|DoClrp;
+ else if(strcmp(argv[i], "venti") == 0)
+ fsck.useventi = 1;
+ else if(strcmp(argv[i], "snapshot") == 0)
+ fsck.walksnapshots = 1;
+ else{
+ consPrint("unknown option '%s'\n", argv[i]);
+ return cliError(usage);
+ }
+ }
+
+ halting = fsys->fs->halted==0;
+ if(halting)
+ fsHalt(fsys->fs);
+ if(fsys->fs->arch){
+ b = superGet(fsys->fs->cache, &super);
+ if(b == nil){
+ consPrint("could not load super block\n");
+ goto Out;
+ }
+ blockPut(b);
+ if(super.current != NilBlock){
+ consPrint("cannot check fs while archiver is running; "
+ "wait for it to finish\n");
+ goto Out;
+ }
+ }
+ fsCheck(&fsck);
+ consPrint("fsck: %d clri, %d clre, %d clrp, %d bclose\n",
+ fsck.nclri, fsck.nclre, fsck.nclrp, fsck.nclose);
+Out:
+ if(halting)
+ fsUnhalt(fsys->fs);
+ return 1;
+}
+
+static int
+fsysVenti(char* name, int argc, char* argv[])
+{
+ int r;
+ char *host;
+ char *usage = "usage: [fsys name] venti [address]";
+ Fsys *fsys;
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+
+ if(argc == 0)
+ host = nil;
+ else if(argc == 1)
+ host = argv[0];
+ else
+ return cliError(usage);
+
+ if((fsys = _fsysGet(name)) == nil)
+ return 0;
+
+ vtLock(fsys->lock);
+ if(host == nil)
+ host = fsys->venti;
+ else{
+ vtMemFree(fsys->venti);
+ if(host[0])
+ fsys->venti = vtStrDup(host);
+ else{
+ host = nil;
+ fsys->venti = nil;
+ }
+ }
+
+ /* already open: do a redial */
+ if(fsys->fs != nil){
+ if(fsys->session == nil){
+ vtSetError("file system was opened with -V");
+ r = 0;
+ goto out;
+ }
+ r = 1;
+ if(!myRedial(fsys->session, host)
+ || !vtConnect(fsys->session, 0))
+ r = 0;
+ goto out;
+ }
+
+ /* not yet open: try to dial */
+ if(fsys->session)
+ vtClose(fsys->session);
+ r = 1;
+ if((fsys->session = myDial(host, 0)) == nil
+ || !vtConnect(fsys->session, 0))
+ r = 0;
+out:
+ vtUnlock(fsys->lock);
+ fsysPut(fsys);
+ return r;
+}
+
+static ulong
+freemem(void)
+{
+ int nf, pgsize = 0;
+ uvlong size, userpgs = 0, userused = 0;
+ char *ln, *sl;
+ char *fields[2];
+ Biobuf *bp;
+
+ size = 64*1024*1024;
+ bp = Bopen("#c/swap", OREAD);
+ if (bp != nil) {
+ while ((ln = Brdline(bp, '\n')) != nil) {
+ ln[Blinelen(bp)-1] = '\0';
+ nf = tokenize(ln, fields, nelem(fields));
+ if (nf != 2)
+ continue;
+ if (strcmp(fields[1], "pagesize") == 0)
+ pgsize = atoi(fields[0]);
+ else if (strcmp(fields[1], "user") == 0) {
+ sl = strchr(fields[0], '/');
+ if (sl == nil)
+ continue;
+ userpgs = atoll(sl+1);
+ userused = atoll(fields[0]);
+ }
+ }
+ Bterm(bp);
+ if (pgsize > 0 && userpgs > 0)
+ size = (userpgs - userused) * pgsize;
+ }
+ /* cap it to keep the size within 32 bits */
+ if (size >= 3840UL * 1024 * 1024)
+ size = 3840UL * 1024 * 1024;
+ return size;
+}
+
+static int
+fsysOpen(char* name, int argc, char* argv[])
+{
+ char *p, *host;
+ Fsys *fsys;
+ int noauth, noventi, noperm, rflag, wstatallow, noatimeupd;
+ long ncache;
+ char *usage = "usage: fsys name open [-APVWr] [-c ncache]";
+
+ ncache = 1000;
+ noauth = noperm = wstatallow = noventi = noatimeupd = 0;
+ rflag = OReadWrite;
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ case 'A':
+ noauth = 1;
+ break;
+ case 'P':
+ noperm = 1;
+ break;
+ case 'V':
+ noventi = 1;
+ break;
+ case 'W':
+ wstatallow = 1;
+ break;
+ case 'a':
+ noatimeupd = 1;
+ break;
+ case 'c':
+ p = ARGF();
+ if(p == nil)
+ return cliError(usage);
+ ncache = strtol(argv[0], &p, 0);
+ if(ncache <= 0 || p == argv[0] || *p != '\0')
+ return cliError(usage);
+ break;
+ case 'r':
+ rflag = OReadOnly;
+ break;
+ }ARGEND
+ if(argc)
+ return cliError(usage);
+
+ if((fsys = _fsysGet(name)) == nil)
+ return 0;
+
+ /* automatic memory sizing? */
+ if(mempcnt > 0) {
+ /* TODO: 8K is a hack; use the actual block size */
+ ncache = (((vlong)freemem() * mempcnt) / 100) / (8*1024);
+ if (ncache < 100)
+ ncache = 100;
+ }
+
+ vtLock(fsys->lock);
+ if(fsys->fs != nil){
+ vtSetError(EFsysBusy, fsys->name);
+ vtUnlock(fsys->lock);
+ fsysPut(fsys);
+ return 0;
+ }
+
+ if(noventi){
+ if(fsys->session){
+ vtClose(fsys->session);
+ fsys->session = nil;
+ }
+ }
+ else if(fsys->session == nil){
+ if(fsys->venti && fsys->venti[0])
+ host = fsys->venti;
+ else
+ host = nil;
+ fsys->session = myDial(host, 1);
+ if(!vtConnect(fsys->session, nil) && !noventi)
+ fprint(2, "warning: connecting to venti: %R\n");
+ }
+ if((fsys->fs = fsOpen(fsys->dev, fsys->session, ncache, rflag)) == nil){
+ vtSetError("fsOpen: %R");
+ vtUnlock(fsys->lock);
+ fsysPut(fsys);
+ return 0;
+ }
+ fsys->fs->name = fsys->name; /* for better error messages */
+ fsys->noauth = noauth;
+ fsys->noperm = noperm;
+ fsys->wstatallow = wstatallow;
+ fsys->fs->noatimeupd = noatimeupd;
+ vtUnlock(fsys->lock);
+ fsysPut(fsys);
+
+ if(strcmp(name, "main") == 0)
+ usersFileRead(nil);
+
+ return 1;
+}
+
+static int
+fsysUnconfig(char* name, int argc, char* argv[])
+{
+ Fsys *fsys, **fp;
+ char *usage = "usage: fsys name unconfig";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc)
+ return cliError(usage);
+
+ vtLock(sbox.lock);
+ fp = &sbox.head;
+ for(fsys = *fp; fsys != nil; fsys = fsys->next){
+ if(strcmp(fsys->name, name) == 0)
+ break;
+ fp = &fsys->next;
+ }
+ if(fsys == nil){
+ vtSetError(EFsysNotFound, name);
+ vtUnlock(sbox.lock);
+ return 0;
+ }
+ if(fsys->ref != 0 || fsys->fs != nil){
+ vtSetError(EFsysBusy, fsys->name);
+ vtUnlock(sbox.lock);
+ return 0;
+ }
+ *fp = fsys->next;
+ vtUnlock(sbox.lock);
+
+ if(fsys->session != nil){
+ vtClose(fsys->session);
+ vtFree(fsys->session);
+ }
+ if(fsys->venti != nil)
+ vtMemFree(fsys->venti);
+ if(fsys->dev != nil)
+ vtMemFree(fsys->dev);
+ if(fsys->name != nil)
+ vtMemFree(fsys->name);
+ vtMemFree(fsys);
+
+ return 1;
+}
+
+static int
+fsysConfig(char* name, int argc, char* argv[])
+{
+ Fsys *fsys;
+ char *part;
+ char *usage = "usage: fsys name config [dev]";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+ if(argc > 1)
+ return cliError(usage);
+
+ if(argc == 0)
+ part = foptname;
+ else
+ part = argv[0];
+
+ if((fsys = _fsysGet(part)) != nil){
+ vtLock(fsys->lock);
+ if(fsys->fs != nil){
+ vtSetError(EFsysBusy, fsys->name);
+ vtUnlock(fsys->lock);
+ fsysPut(fsys);
+ return 0;
+ }
+ vtMemFree(fsys->dev);
+ fsys->dev = vtStrDup(part);
+ vtUnlock(fsys->lock);
+ }
+ else if((fsys = fsysAlloc(name, part)) == nil)
+ return 0;
+
+ fsysPut(fsys);
+ return 1;
+}
+
+static struct {
+ char* cmd;
+ int (*f)(Fsys*, int, char**);
+ int (*f1)(char*, int, char**);
+} fsyscmd[] = {
+ { "close", fsysClose, },
+ { "config", nil, fsysConfig, },
+ { "open", nil, fsysOpen, },
+ { "unconfig", nil, fsysUnconfig, },
+ { "venti", nil, fsysVenti, },
+
+ { "bfree", fsysBfree, },
+ { "block", fsysBlock, },
+ { "check", fsysCheck, },
+ { "clre", fsysClre, },
+ { "clri", fsysClri, },
+ { "clrp", fsysClrp, },
+ { "create", fsysCreate, },
+ { "df", fsysDf, },
+ { "epoch", fsysEpoch, },
+ { "halt", fsysHalt, },
+ { "label", fsysLabel, },
+ { "remove", fsysRemove, },
+ { "snap", fsysSnap, },
+ { "snaptime", fsysSnapTime, },
+ { "snapclean", fsysSnapClean, },
+ { "stat", fsysStat, },
+ { "sync", fsysSync, },
+ { "unhalt", fsysUnhalt, },
+ { "wstat", fsysWstat, },
+ { "vac", fsysVac, },
+
+ { nil, nil, },
+};
+
+static int
+fsysXXX1(Fsys *fsys, int i, int argc, char* argv[])
+{
+ int r;
+
+ vtLock(fsys->lock);
+ if(fsys->fs == nil){
+ vtUnlock(fsys->lock);
+ vtSetError(EFsysNotOpen, fsys->name);
+ return 0;
+ }
+
+ if(fsys->fs->halted
+ && fsyscmd[i].f != fsysUnhalt && fsyscmd[i].f != fsysCheck){
+ vtSetError("file system %s is halted", fsys->name);
+ vtUnlock(fsys->lock);
+ return 0;
+ }
+
+ r = (*fsyscmd[i].f)(fsys, argc, argv);
+ vtUnlock(fsys->lock);
+ return r;
+}
+
+static int
+fsysXXX(char* name, int argc, char* argv[])
+{
+ int i, r;
+ Fsys *fsys;
+
+ for(i = 0; fsyscmd[i].cmd != nil; i++){
+ if(strcmp(fsyscmd[i].cmd, argv[0]) == 0)
+ break;
+ }
+
+ if(fsyscmd[i].cmd == nil){
+ vtSetError("unknown command - '%s'", argv[0]);
+ return 0;
+ }
+
+ /* some commands want the name... */
+ if(fsyscmd[i].f1 != nil){
+ if(strcmp(name, FsysAll) == 0){
+ vtSetError("cannot use fsys %#q with %#q command", FsysAll, argv[0]);
+ return 0;
+ }
+ return (*fsyscmd[i].f1)(name, argc, argv);
+ }
+
+ /* ... but most commands want the Fsys */
+ if(strcmp(name, FsysAll) == 0){
+ r = 1;
+ vtRLock(sbox.lock);
+ for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
+ fsys->ref++;
+ r = fsysXXX1(fsys, i, argc, argv) && r;
+ fsys->ref--;
+ }
+ vtRUnlock(sbox.lock);
+ }else{
+ if((fsys = _fsysGet(name)) == nil)
+ return 0;
+ r = fsysXXX1(fsys, i, argc, argv);
+ fsysPut(fsys);
+ }
+ return r;
+}
+
+static int
+cmdFsysXXX(int argc, char* argv[])
+{
+ char *name;
+
+ if((name = sbox.curfsys) == nil){
+ vtSetError(EFsysNoCurrent, argv[0]);
+ return 0;
+ }
+
+ return fsysXXX(name, argc, argv);
+}
+
+static int
+cmdFsys(int argc, char* argv[])
+{
+ Fsys *fsys;
+ char *usage = "usage: fsys [name ...]";
+
+ ARGBEGIN{
+ default:
+ return cliError(usage);
+ }ARGEND
+
+ if(argc == 0){
+ vtRLock(sbox.lock);
+ currfsysname = sbox.head->name;
+ for(fsys = sbox.head; fsys != nil; fsys = fsys->next)
+ consPrint("\t%s\n", fsys->name);
+ vtRUnlock(sbox.lock);
+ return 1;
+ }
+ if(argc == 1){
+ fsys = nil;
+ if(strcmp(argv[0], FsysAll) != 0 && (fsys = fsysGet(argv[0])) == nil)
+ return 0;
+ sbox.curfsys = vtStrDup(argv[0]);
+ consPrompt(sbox.curfsys);
+ if(fsys)
+ fsysPut(fsys);
+ return 1;
+ }
+
+ return fsysXXX(argv[0], argc-1, argv+1);
+}
+
+int
+fsysInit(void)
+{
+ int i;
+
+ fmtinstall('H', encodefmt);
+ fmtinstall('V', scoreFmt);
+ fmtinstall('R', vtErrFmt);
+ fmtinstall('L', labelFmt);
+
+ sbox.lock = vtLockAlloc();
+
+ cliAddCmd("fsys", cmdFsys);
+ for(i = 0; fsyscmd[i].cmd != nil; i++){
+ if(fsyscmd[i].f != nil)
+ cliAddCmd(fsyscmd[i].cmd, cmdFsysXXX);
+ }
+ /* the venti cmd is special: the fs can be either open or closed */
+ cliAddCmd("venti", cmdFsysXXX);
+ cliAddCmd("printconfig", cmdPrintConfig);
+
+ return 1;
+}