| #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 VtConn *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 |
| threadmain(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) |
| sysfatal("cannot use -i with -v"); |
| |
| fmtinstall('V', scoreFmt); |
| fmtinstall('L', labelFmt); |
| |
| fd = open(argv[0], ORDWR); |
| if(fd < 0) |
| sysfatal("could not open file: %s: %r", argv[0]); |
| |
| buf = vtmallocz(bsize); |
| if(pread(fd, buf, bsize, HeaderOffset) != bsize) |
| sysfatal("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) |
| sysfatal("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) |
| sysfatal("could not write fs header: %r"); |
| |
| disk = diskAlloc(fd); |
| if(disk == nil) |
| sysfatal("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: |
| threadexitsall(0); |
| } |
| |
| static u64int |
| fdsize(int fd) |
| { |
| Dir *dir; |
| u64int size; |
| |
| dir = dirfstat(fd); |
| if(dir == nil) |
| sysfatal("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); |
| threadexitsall("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) |
| sysfatal("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)) |
| sysfatal("bad label: %r"); |
| if(l.state != BsFree) |
| sysfatal("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)) |
| sysfatal("read failed: %r"); |
| } |
| |
| static void |
| blockWrite(int part, u32int addr) |
| { |
| if(!diskWriteRaw(disk, part, addr, buf)) |
| sysfatal("write failed: %r"); |
| } |
| |
| static void |
| addFile(File *root, char *name, uint mode) |
| { |
| File *f; |
| |
| f = fileCreate(root, name, mode | ModeDir, "adm"); |
| if(f == nil) |
| sysfatal("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) |
| sysfatal("could not open file system: %r"); |
| rlock(&fs->elk); |
| root = fsGetRoot(fs); |
| if(root == nil) |
| sysfatal("could not open root: %r"); |
| addFile(root, "active", 0555); |
| addFile(root, "archive", 0555); |
| addFile(root, "snapshot", 0555); |
| fileDecRef(root); |
| if(iso9660file) |
| iso9660copy(fs); |
| runlock(&fs->elk); |
| fsClose(fs); |
| } |
| |
| static int |
| ventiRead(uchar score[VtScoreSize], int type) |
| { |
| int n; |
| |
| n = vtread(z, score, type, buf, bsize); |
| if(n < 0) |
| sysfatal("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)) |
| sysfatal("bad score '%s'", s); |
| |
| if((z = vtdial(host)) == nil |
| || vtconnect(z) < 0) |
| sysfatal("connect to venti: %r"); |
| |
| tag = tagGen(); |
| addr = blockAlloc(BtDir, tag); |
| |
| ventiRead(score, VtRootType); |
| if(vtrootunpack(&root, buf) < 0) |
| sysfatal("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)) |
| sysfatal("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) |
| sysfatal("bad root: entry %d", i); |
| fprint(2, "%V\n", e.score); |
| } |
| if(n > 3*VtEntrySize) |
| sysfatal("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)) |
| sysfatal("bad root: mbUnpack"); |
| meUnpack(&me, &mb, 0); |
| if(!deUnpack(&de, &me)) |
| sysfatal("bad root: dirUnpack"); |
| if(!de.qidSpace) |
| sysfatal("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; |
| } |