diff options
author | David du Colombier <0intro@gmail.com> | 2013-09-23 23:00:39 +0200 |
---|---|---|
committer | David du Colombier <0intro@gmail.com> | 2013-09-23 23:00:39 +0200 |
commit | 6f4d00ee45693290fae042b27536b54f77b96acd (patch) | |
tree | 60ad31bf16ed2000661c02345dd2a63851588a5d /src/cmd/fossil/9fsys.c | |
parent | fea86f063930ea187f1c77e93207ac8d39125520 (diff) | |
download | plan9port-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.c | 1896 |
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; +} |