#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 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= '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; }