| #include "stdinc.h" |
| #include <bio.h> |
| #include "dat.h" |
| #include "fns.h" |
| #include "9.h" |
| |
| struct Fsys { |
| QLock lock; |
| |
| char* name; /* copy here & Fs to ease error reporting */ |
| char* dev; |
| char* venti; |
| |
| Fs* fs; |
| VtConn* session; |
| int ref; |
| |
| int noauth; |
| int noperm; |
| int wstatallow; |
| |
| Fsys* next; |
| }; |
| |
| int mempcnt; /* from fossil.c */ |
| |
| int fsGetBlockSize(Fs *fs); |
| |
| static struct { |
| RWLock 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 VtConn * |
| myDial(char *host) |
| { |
| prventihost(host); |
| return vtdial(host); |
| } |
| |
| static int |
| myRedial(VtConn *z, char *host) |
| { |
| prventihost(host); |
| return vtredial(z, host); |
| } |
| |
| static Fsys* |
| _fsysGet(char* name) |
| { |
| Fsys *fsys; |
| |
| if(name == nil || name[0] == '\0') |
| name = "main"; |
| |
| rlock(&sbox.lock); |
| for(fsys = sbox.head; fsys != nil; fsys = fsys->next){ |
| if(strcmp(name, fsys->name) == 0){ |
| fsys->ref++; |
| break; |
| } |
| } |
| runlock(&sbox.lock); |
| if(fsys == nil) |
| werrstr(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); |
| |
| rlock(&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); |
| } |
| runlock(&sbox.lock); |
| return 1; |
| } |
| |
| Fsys* |
| fsysGet(char* name) |
| { |
| Fsys *fsys; |
| |
| if((fsys = _fsysGet(name)) == nil) |
| return nil; |
| |
| qlock(&fsys->lock); |
| if(fsys->fs == nil){ |
| werrstr(EFsysNotOpen, fsys->name); |
| qunlock(&fsys->lock); |
| fsysPut(fsys); |
| return nil; |
| } |
| qunlock(&fsys->lock); |
| |
| return fsys; |
| } |
| |
| char* |
| fsysGetName(Fsys* fsys) |
| { |
| return fsys->name; |
| } |
| |
| Fsys* |
| fsysIncRef(Fsys* fsys) |
| { |
| wlock(&sbox.lock); |
| fsys->ref++; |
| wunlock(&sbox.lock); |
| |
| return fsys; |
| } |
| |
| void |
| fsysPut(Fsys* fsys) |
| { |
| wlock(&sbox.lock); |
| assert(fsys->ref > 0); |
| fsys->ref--; |
| wunlock(&sbox.lock); |
| } |
| |
| Fs* |
| fsysGetFs(Fsys* fsys) |
| { |
| assert(fsys != nil && fsys->fs != nil); |
| |
| return fsys->fs; |
| } |
| |
| void |
| fsysFsRlock(Fsys* fsys) |
| { |
| rlock(&fsys->fs->elk); |
| } |
| |
| void |
| fsysFsRUnlock(Fsys* fsys) |
| { |
| runlock(&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; |
| |
| wlock(&sbox.lock); |
| for(fsys = sbox.head; fsys != nil; fsys = fsys->next){ |
| if(strcmp(fsys->name, name) != 0) |
| continue; |
| werrstr(EFsysExists, name); |
| wunlock(&sbox.lock); |
| return nil; |
| } |
| |
| fsys = vtmallocz(sizeof(Fsys)); |
| 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; |
| wunlock(&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; |
| vtfreeconn(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); |
| |
| rlock(&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++; |
| } |
| runlock(&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); |
| |
| rlock(&fsys->fs->elk); |
| while(argc > 0){ |
| if(!fileClriPath(fsys->fs, argv[0], uidadm)) |
| consPrint("clri %s: %r\n", argv[0]); |
| argc--; |
| argv++; |
| } |
| runlock(&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; |
| rlock(&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); |
| rsleep(&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: |
| runlock(&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){ |
| werrstr("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; |
| |
| rlock(&fs->elk); |
| |
| b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly); |
| if(b == nil){ |
| werrstr("cacheLocal %#ux: %r", addr); |
| runlock(&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){ |
| werrstr("bad data count"); |
| goto Out; |
| } |
| buf = vtmallocz(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{ |
| werrstr("bad hex"); |
| vtfree(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); |
| runlock(&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; |
| rlock(&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 */ |
| runlock(&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++; |
| } |
| runlock(&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; |
| rlock(&fsys->fs->elk); |
| |
| addr = strtoul(argv[0], 0, 0); |
| b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly); |
| if(b == nil){ |
| werrstr("cacheLocal %#ux: %r", addr); |
| Err: |
| runlock(&fsys->fs->elk); |
| return 0; |
| } |
| |
| switch(ch){ |
| default: |
| werrstr("clrep"); |
| goto Err; |
| case 'e': |
| if(b->l.type != BtDir){ |
| werrstr("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){ |
| werrstr("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); |
| runlock(&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); |
| vtfree(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; |
| |
| rlock(&fs->elk); |
| consPrint("\tlow %ud hi %ud\n", fs->elo, fs->ehi); |
| if(low == ~(u32int)0){ |
| runlock(&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); |
| runlock(&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"); |
| |
| rlock(&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){ |
| werrstr("create %s/%s: %r", p, elem); |
| goto out; |
| } |
| |
| if(!fileGetDir(file, &de)){ |
| werrstr("stat failed after create: %r"); |
| goto out1; |
| } |
| |
| if(strcmp(de.gid, argv[2]) != 0){ |
| vtfree(de.gid); |
| de.gid = vtstrdup(argv[2]); |
| if(!fileSetDir(file, &de, argv[1])){ |
| werrstr("wstat failed after create: %r"); |
| goto out2; |
| } |
| } |
| r = 1; |
| |
| out2: |
| deCleanup(&de); |
| out1: |
| fileDecRef(file); |
| out: |
| vtfree(path); |
| runlock(&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); |
| |
| rlock(&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); |
| } |
| runlock(&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); |
| |
| rlock(&fsys->fs->elk); |
| if((f = fileOpen(fsys->fs, argv[0])) == nil){ |
| werrstr("console wstat - walk - %r"); |
| runlock(&fsys->fs->elk); |
| return 0; |
| } |
| if(!fileGetDir(f, &de)){ |
| werrstr("console wstat - stat - %r"); |
| fileDecRef(f); |
| runlock(&fsys->fs->elk); |
| return 0; |
| } |
| fsysPrintStat("\told: w", argv[0], &de); |
| |
| if(strcmp(argv[1], "-") != 0){ |
| if(!validFileName(argv[1])){ |
| werrstr("console wstat - bad elem"); |
| goto error; |
| } |
| vtfree(de.elem); |
| de.elem = vtstrdup(argv[1]); |
| } |
| if(strcmp(argv[2], "-") != 0){ |
| if(!validUserName(argv[2])){ |
| werrstr("console wstat - bad uid"); |
| goto error; |
| } |
| vtfree(de.uid); |
| de.uid = vtstrdup(argv[2]); |
| } |
| if(strcmp(argv[3], "-") != 0){ |
| if(!validUserName(argv[3])){ |
| werrstr("console wstat - bad gid"); |
| goto error; |
| } |
| vtfree(de.gid); |
| de.gid = vtstrdup(argv[3]); |
| } |
| if(strcmp(argv[4], "-") != 0){ |
| if(!fsysParseMode(argv[4], &de.mode)){ |
| werrstr("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){ |
| werrstr("console wstat - bad length"); |
| goto error; |
| } |
| } |
| |
| if(!fileSetDir(f, &de, uidadm)){ |
| werrstr("console wstat - %r"); |
| goto error; |
| } |
| deCleanup(&de); |
| |
| if(!fileGetDir(f, &de)){ |
| werrstr("console wstat - stat2 - %r"); |
| goto error; |
| } |
| fsysPrintStat("\tnew: w", argv[0], &de); |
| deCleanup(&de); |
| fileDecRef(f); |
| runlock(&fsys->fs->elk); |
| |
| return 1; |
| |
| error: |
| deCleanup(&de); /* okay to do this twice */ |
| fileDecRef(f); |
| runlock(&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; |
| |
| qlock(&fsys->lock); |
| if(host == nil) |
| host = fsys->venti; |
| else{ |
| vtfree(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){ |
| werrstr("file system was opened with -V"); |
| r = 0; |
| goto out; |
| } |
| r = 1; |
| if(myRedial(fsys->session, host) < 0 |
| || vtconnect(fsys->session) < 0) |
| r = 0; |
| goto out; |
| } |
| |
| /* not yet open: try to dial */ |
| if(fsys->session) |
| vtfreeconn(fsys->session); |
| r = 1; |
| if((fsys->session = myDial(host)) == nil |
| || vtconnect(fsys->session) < 0) |
| r = 0; |
| out: |
| qunlock(&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; |
| } |
| |
| qlock(&fsys->lock); |
| if(fsys->fs != nil){ |
| werrstr(EFsysBusy, fsys->name); |
| qunlock(&fsys->lock); |
| fsysPut(fsys); |
| return 0; |
| } |
| |
| if(noventi){ |
| if(fsys->session){ |
| vtfreeconn(fsys->session); |
| fsys->session = nil; |
| } |
| } |
| else if(fsys->session == nil){ |
| if(fsys->venti && fsys->venti[0]) |
| host = fsys->venti; |
| else |
| host = nil; |
| |
| if((fsys->session = myDial(host)) == nil |
| || vtconnect(fsys->session) < 0 && !noventi) |
| fprint(2, "warning: connecting to venti: %r\n"); |
| } |
| if((fsys->fs = fsOpen(fsys->dev, fsys->session, ncache, rflag)) == nil){ |
| werrstr("fsOpen: %r"); |
| qunlock(&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; |
| qunlock(&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); |
| |
| wlock(&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){ |
| werrstr(EFsysNotFound, name); |
| wunlock(&sbox.lock); |
| return 0; |
| } |
| if(fsys->ref != 0 || fsys->fs != nil){ |
| werrstr(EFsysBusy, fsys->name); |
| wunlock(&sbox.lock); |
| return 0; |
| } |
| *fp = fsys->next; |
| wunlock(&sbox.lock); |
| |
| if(fsys->session != nil) |
| vtfreeconn(fsys->session); |
| if(fsys->venti != nil) |
| vtfree(fsys->venti); |
| if(fsys->dev != nil) |
| vtfree(fsys->dev); |
| if(fsys->name != nil) |
| vtfree(fsys->name); |
| vtfree(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){ |
| qlock(&fsys->lock); |
| if(fsys->fs != nil){ |
| werrstr(EFsysBusy, fsys->name); |
| qunlock(&fsys->lock); |
| fsysPut(fsys); |
| return 0; |
| } |
| vtfree(fsys->dev); |
| fsys->dev = vtstrdup(part); |
| qunlock(&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; |
| |
| qlock(&fsys->lock); |
| if(fsys->fs == nil){ |
| qunlock(&fsys->lock); |
| werrstr(EFsysNotOpen, fsys->name); |
| return 0; |
| } |
| |
| if(fsys->fs->halted |
| && fsyscmd[i].f != fsysUnhalt && fsyscmd[i].f != fsysCheck){ |
| werrstr("file system %s is halted", fsys->name); |
| qunlock(&fsys->lock); |
| return 0; |
| } |
| |
| r = (*fsyscmd[i].f)(fsys, argc, argv); |
| qunlock(&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){ |
| werrstr("unknown command - '%s'", argv[0]); |
| return 0; |
| } |
| |
| /* some commands want the name... */ |
| if(fsyscmd[i].f1 != nil){ |
| if(strcmp(name, FsysAll) == 0){ |
| werrstr("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; |
| rlock(&sbox.lock); |
| for(fsys = sbox.head; fsys != nil; fsys = fsys->next){ |
| fsys->ref++; |
| r = fsysXXX1(fsys, i, argc, argv) && r; |
| fsys->ref--; |
| } |
| runlock(&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){ |
| werrstr(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){ |
| rlock(&sbox.lock); |
| currfsysname = sbox.head->name; |
| for(fsys = sbox.head; fsys != nil; fsys = fsys->next) |
| consPrint("\t%s\n", fsys->name); |
| runlock(&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('L', labelFmt); |
| |
| 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; |
| } |