diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/fossil/flfmt.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/fossil/flfmt.c')
-rwxr-xr-x | sys/src/cmd/fossil/flfmt.c | 572 |
1 files changed, 572 insertions, 0 deletions
diff --git a/sys/src/cmd/fossil/flfmt.c b/sys/src/cmd/fossil/flfmt.c new file mode 100755 index 000000000..b843388a0 --- /dev/null +++ b/sys/src/cmd/fossil/flfmt.c @@ -0,0 +1,572 @@ +#include "stdinc.h" +#include "dat.h" +#include "fns.h" +#include "flfmt9660.h" + +#define blockWrite _blockWrite /* hack */ + +static void usage(void); +static u64int fdsize(int fd); +static void partition(int fd, int bsize, Header *h); +static void writeBlock(int fd, uchar *buf, int bsize, ulong bn); +static u64int unittoull(char *s); +static u32int blockAlloc(int type, u32int tag); +static void blockRead(int part, u32int addr); +static void blockWrite(int part, u32int addr); +static void superInit(char *label, u32int root, uchar[VtScoreSize]); +static void rootMetaInit(Entry *e); +static u32int rootInit(Entry *e); +static void topLevel(char *name); +static int parseScore(uchar[VtScoreSize], char*); +static u32int ventiRoot(char*, char*); +static VtSession *z; + +#define TWID64 ((u64int)~(u64int)0) + +Disk *disk; +Fs *fs; +uchar *buf; +int bsize = 8*1024; +u64int qid = 1; +int iso9660off; +char *iso9660file; + +int +confirm(char *msg) +{ + char buf[100]; + int n; + + fprint(2, "%s [y/n]: ", msg); + n = read(0, buf, sizeof buf - 1); + if(n <= 0) + return 0; + if(buf[0] == 'y') + return 1; + return 0; +} + +void +main(int argc, char *argv[]) +{ + int fd, force; + Header h; + ulong bn; + Entry e; + char *label = "vfs"; + char *host = nil; + char *score = nil; + u32int root; + Dir *d; + + force = 0; + ARGBEGIN{ + default: + usage(); + case 'b': + bsize = unittoull(EARGF(usage())); + if(bsize == ~0) + usage(); + break; + case 'h': + host = EARGF(usage()); + break; + case 'i': + iso9660file = EARGF(usage()); + iso9660off = atoi(EARGF(usage())); + break; + case 'l': + label = EARGF(usage()); + break; + case 'v': + score = EARGF(usage()); + break; + + /* + * This is -y instead of -f because flchk has a + * (frequently used) -f option. I type flfmt instead + * of flchk all the time, and want to make it hard + * to reformat my file system accidentally. + */ + case 'y': + force = 1; + break; + }ARGEND + + if(argc != 1) + usage(); + + if(iso9660file && score) + vtFatal("cannot use -i with -v"); + + vtAttach(); + + fmtinstall('V', scoreFmt); + fmtinstall('R', vtErrFmt); + fmtinstall('L', labelFmt); + + fd = open(argv[0], ORDWR); + if(fd < 0) + vtFatal("could not open file: %s: %r", argv[0]); + + buf = vtMemAllocZ(bsize); + if(pread(fd, buf, bsize, HeaderOffset) != bsize) + vtFatal("could not read fs header block: %r"); + + if(headerUnpack(&h, buf) && !force + && !confirm("fs header block already exists; are you sure?")) + goto Out; + + if((d = dirfstat(fd)) == nil) + vtFatal("dirfstat: %r"); + + if(d->type == 'M' && !force + && !confirm("fs file is mounted via devmnt (is not a kernel device); are you sure?")) + goto Out; + + partition(fd, bsize, &h); + headerPack(&h, buf); + if(pwrite(fd, buf, bsize, HeaderOffset) < bsize) + vtFatal("could not write fs header: %r"); + + disk = diskAlloc(fd); + if(disk == nil) + vtFatal("could not open disk: %r"); + + if(iso9660file) + iso9660init(fd, &h, iso9660file, iso9660off); + + /* zero labels */ + memset(buf, 0, bsize); + for(bn = 0; bn < diskSize(disk, PartLabel); bn++) + blockWrite(PartLabel, bn); + + if(iso9660file) + iso9660labels(disk, buf, blockWrite); + + if(score) + root = ventiRoot(host, score); + else{ + rootMetaInit(&e); + root = rootInit(&e); + } + + superInit(label, root, vtZeroScore); + diskFree(disk); + + if(score == nil) + topLevel(argv[0]); + +Out: + vtDetach(); + exits(0); +} + +static u64int +fdsize(int fd) +{ + Dir *dir; + u64int size; + + dir = dirfstat(fd); + if(dir == nil) + vtFatal("could not stat file: %r"); + size = dir->length; + free(dir); + return size; +} + +static void +usage(void) +{ + fprint(2, "usage: %s [-b blocksize] [-h host] [-i file offset] " + "[-l label] [-v score] [-y] file\n", argv0); + exits("usage"); +} + +static void +partition(int fd, int bsize, Header *h) +{ + ulong nblock, ndata, nlabel; + ulong lpb; + + if(bsize % 512 != 0) + sysfatal("block size must be a multiple of 512 bytes"); + if(bsize > VtMaxLumpSize) + sysfatal("block size must be less than %d", VtMaxLumpSize); + + memset(h, 0, sizeof(*h)); + h->blockSize = bsize; + + lpb = bsize/LabelSize; + + nblock = fdsize(fd)/bsize; + + /* sanity check */ + if(nblock < (HeaderOffset*10)/bsize) + vtFatal("file too small"); + + h->super = (HeaderOffset + 2*bsize)/bsize; + h->label = h->super + 1; + ndata = ((u64int)lpb)*(nblock - h->label)/(lpb+1); + nlabel = (ndata + lpb - 1)/lpb; + h->data = h->label + nlabel; + h->end = h->data + ndata; + +} + +static u32int +tagGen(void) +{ + u32int tag; + + for(;;){ + tag = lrand(); + if(tag > RootTag) + break; + } + return tag; +} + +static void +entryInit(Entry *e) +{ + e->gen = 0; + e->dsize = bsize; + e->psize = bsize/VtEntrySize*VtEntrySize; + e->flags = VtEntryActive; + e->depth = 0; + e->size = 0; + memmove(e->score, vtZeroScore, VtScoreSize); + e->tag = tagGen(); + e->snap = 0; + e->archive = 0; +} + +static void +rootMetaInit(Entry *e) +{ + u32int addr; + u32int tag; + DirEntry de; + MetaBlock mb; + MetaEntry me; + + memset(&de, 0, sizeof(de)); + de.elem = vtStrDup("root"); + de.entry = 0; + de.gen = 0; + de.mentry = 1; + de.mgen = 0; + de.size = 0; + de.qid = qid++; + de.uid = vtStrDup("adm"); + de.gid = vtStrDup("adm"); + de.mid = vtStrDup("adm"); + de.mtime = time(0); + de.mcount = 0; + de.ctime = time(0); + de.atime = time(0); + de.mode = ModeDir | 0555; + + tag = tagGen(); + addr = blockAlloc(BtData, tag); + + /* build up meta block */ + memset(buf, 0, bsize); + mbInit(&mb, buf, bsize, bsize/100); + me.size = deSize(&de); + me.p = mbAlloc(&mb, me.size); + assert(me.p != nil); + dePack(&de, &me); + mbInsert(&mb, 0, &me); + mbPack(&mb); + blockWrite(PartData, addr); + deCleanup(&de); + + /* build up entry for meta block */ + entryInit(e); + e->flags |= VtEntryLocal; + e->size = bsize; + e->tag = tag; + localToGlobal(addr, e->score); +} + +static u32int +rootInit(Entry *e) +{ + ulong addr; + u32int tag; + + tag = tagGen(); + + addr = blockAlloc(BtDir, tag); + memset(buf, 0, bsize); + + /* root meta data is in the third entry */ + entryPack(e, buf, 2); + + entryInit(e); + e->flags |= VtEntryDir; + entryPack(e, buf, 0); + + entryInit(e); + entryPack(e, buf, 1); + + blockWrite(PartData, addr); + + entryInit(e); + e->flags |= VtEntryLocal|VtEntryDir; + e->size = VtEntrySize*3; + e->tag = tag; + localToGlobal(addr, e->score); + + addr = blockAlloc(BtDir, RootTag); + memset(buf, 0, bsize); + entryPack(e, buf, 0); + + blockWrite(PartData, addr); + + return addr; +} + + +static u32int +blockAlloc(int type, u32int tag) +{ + static u32int addr; + Label l; + int lpb; + + lpb = bsize/LabelSize; + + blockRead(PartLabel, addr/lpb); + if(!labelUnpack(&l, buf, addr % lpb)) + vtFatal("bad label: %r"); + if(l.state != BsFree) + vtFatal("want to allocate block already in use"); + l.epoch = 1; + l.epochClose = ~(u32int)0; + l.type = type; + l.state = BsAlloc; + l.tag = tag; + labelPack(&l, buf, addr % lpb); + blockWrite(PartLabel, addr/lpb); + return addr++; +} + +static void +superInit(char *label, u32int root, uchar score[VtScoreSize]) +{ + Super s; + + memset(buf, 0, bsize); + memset(&s, 0, sizeof(s)); + s.version = SuperVersion; + s.epochLow = 1; + s.epochHigh = 1; + s.qid = qid; + s.active = root; + s.next = NilBlock; + s.current = NilBlock; + strecpy(s.name, s.name+sizeof(s.name), label); + memmove(s.last, score, VtScoreSize); + + superPack(&s, buf); + blockWrite(PartSuper, 0); +} + +static u64int +unittoull(char *s) +{ + char *es; + u64int n; + + if(s == nil) + return TWID64; + n = strtoul(s, &es, 0); + if(*es == 'k' || *es == 'K'){ + n *= 1024; + es++; + }else if(*es == 'm' || *es == 'M'){ + n *= 1024*1024; + es++; + }else if(*es == 'g' || *es == 'G'){ + n *= 1024*1024*1024; + es++; + } + if(*es != '\0') + return TWID64; + return n; +} + +static void +blockRead(int part, u32int addr) +{ + if(!diskReadRaw(disk, part, addr, buf)) + vtFatal("read failed: %r"); +} + +static void +blockWrite(int part, u32int addr) +{ + if(!diskWriteRaw(disk, part, addr, buf)) + vtFatal("write failed: %r"); +} + +static void +addFile(File *root, char *name, uint mode) +{ + File *f; + + f = fileCreate(root, name, mode | ModeDir, "adm"); + if(f == nil) + vtFatal("could not create file: %s: %r", name); + fileDecRef(f); +} + +static void +topLevel(char *name) +{ + Fs *fs; + File *root; + + /* ok, now we can open as a fs */ + fs = fsOpen(name, z, 100, OReadWrite); + if(fs == nil) + vtFatal("could not open file system: %r"); + vtRLock(fs->elk); + root = fsGetRoot(fs); + if(root == nil) + vtFatal("could not open root: %r"); + addFile(root, "active", 0555); + addFile(root, "archive", 0555); + addFile(root, "snapshot", 0555); + fileDecRef(root); + if(iso9660file) + iso9660copy(fs); + vtRUnlock(fs->elk); + fsClose(fs); +} + +static int +ventiRead(uchar score[VtScoreSize], int type) +{ + int n; + + n = vtRead(z, score, type, buf, bsize); + if(n < 0) + vtFatal("ventiRead %V (%d) failed: %R", score, type); + vtZeroExtend(type, buf, n, bsize); + return n; +} + +static u32int +ventiRoot(char *host, char *s) +{ + int i, n; + uchar score[VtScoreSize]; + u32int addr, tag; + DirEntry de; + MetaBlock mb; + MetaEntry me; + Entry e; + VtRoot root; + + if(!parseScore(score, s)) + vtFatal("bad score '%s'", s); + + if((z = vtDial(host, 0)) == nil + || !vtConnect(z, nil)) + vtFatal("connect to venti: %R"); + + tag = tagGen(); + addr = blockAlloc(BtDir, tag); + + ventiRead(score, VtRootType); + if(!vtRootUnpack(&root, buf)) + vtFatal("corrupted root: vtRootUnpack"); + n = ventiRead(root.score, VtDirType); + + /* + * Fossil's vac archives start with an extra layer of source, + * but vac's don't. + */ + if(n <= 2*VtEntrySize){ + if(!entryUnpack(&e, buf, 0)) + vtFatal("bad root: top entry"); + n = ventiRead(e.score, VtDirType); + } + + /* + * There should be three root sources (and nothing else) here. + */ + for(i=0; i<3; i++){ + if(!entryUnpack(&e, buf, i) + || !(e.flags&VtEntryActive) + || e.psize < 256 + || e.dsize < 256) + vtFatal("bad root: entry %d", i); + fprint(2, "%V\n", e.score); + } + if(n > 3*VtEntrySize) + vtFatal("bad root: entry count"); + + blockWrite(PartData, addr); + + /* + * Maximum qid is recorded in root's msource, entry #2 (conveniently in e). + */ + ventiRead(e.score, VtDataType); + if(!mbUnpack(&mb, buf, bsize)) + vtFatal("bad root: mbUnpack"); + meUnpack(&me, &mb, 0); + if(!deUnpack(&de, &me)) + vtFatal("bad root: dirUnpack"); + if(!de.qidSpace) + vtFatal("bad root: no qidSpace"); + qid = de.qidMax; + + /* + * Recreate the top layer of source. + */ + entryInit(&e); + e.flags |= VtEntryLocal|VtEntryDir; + e.size = VtEntrySize*3; + e.tag = tag; + localToGlobal(addr, e.score); + + addr = blockAlloc(BtDir, RootTag); + memset(buf, 0, bsize); + entryPack(&e, buf, 0); + blockWrite(PartData, addr); + + return addr; +} + +static int +parseScore(uchar *score, char *buf) +{ + int i, c; + + memset(score, 0, VtScoreSize); + + if(strlen(buf) < VtScoreSize*2) + return 0; + for(i=0; i<VtScoreSize*2; i++){ + if(buf[i] >= '0' && buf[i] <= '9') + c = buf[i] - '0'; + else if(buf[i] >= 'a' && buf[i] <= 'f') + c = buf[i] - 'a' + 10; + else if(buf[i] >= 'A' && buf[i] <= 'F') + c = buf[i] - 'A' + 10; + else + return 0; + + if((i & 1) == 0) + c <<= 4; + + score[i>>1] |= c; + } + return 1; +} |