summaryrefslogtreecommitdiff
path: root/sys/src/cmd/fossil/flfmt.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /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-xsys/src/cmd/fossil/flfmt.c572
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;
+}