Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

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