568 lines
10 KiB
C
568 lines
10 KiB
C
#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;
|
|
}
|