1 #include "stdinc.h" 2 #include "dat.h" 3 #include "fns.h" 4 5 #define blockWrite _blockWrite /* hack */ 6 7 static void usage(void); 8 static u64int fdsize(int fd); 9 static void partition(int fd, int bsize, Header *h); 10 static void writeBlock(int fd, uchar *buf, int bsize, ulong bn); 11 static u64int unittoull(char *s); 12 static u32int blockAlloc(int type, u32int tag); 13 static void blockRead(int part, u32int addr); 14 static void blockWrite(int part, u32int addr); 15 static void superInit(char *label, u32int root, uchar[VtScoreSize]); 16 static void rootMetaInit(Entry *e); 17 static u32int rootInit(Entry *e); 18 static void topLevel(char *name); 19 static int parseScore(uchar[VtScoreSize], char*); 20 static u32int ventiRoot(char*, char*); 21 static VtSession *z; 22 23 #define TWID64 ((u64int)~(u64int)0) 24 25 Disk *disk; 26 Fs *fs; 27 uchar *buf; 28 int bsize = 8*1024; 29 u64int qid = 1; 30 31 int 32 confirm(char *msg) 33 { 34 char buf[100]; 35 int n; 36 37 fprint(2, "%s [y/n]: ", msg); 38 n = read(0, buf, sizeof buf - 1); 39 if(n <= 0) 40 return 0; 41 if(buf[0] == 'y') 42 return 1; 43 return 0; 44 } 45 46 void 47 main(int argc, char *argv[]) 48 { 49 int fd, force; 50 Header h; 51 ulong bn; 52 Entry e; 53 char *label = "vfs"; 54 char *host = nil; 55 char *score = nil; 56 u32int root; 57 Dir *d; 58 59 force = 0; 60 ARGBEGIN{ 61 default: 62 usage(); 63 case 'b': 64 bsize = unittoull(EARGF(usage())); 65 if(bsize == ~0) 66 usage(); 67 break; 68 case 'h': 69 host = EARGF(usage()); 70 break; 71 case 'l': 72 label = EARGF(usage()); 73 break; 74 case 'v': 75 score = EARGF(usage()); 76 break; 77 78 /* 79 * This is -y instead of -f because flchk has a 80 * (frequently used) -f option. I type flfmt instead 81 * of flchk all the time, and want to make it hard 82 * to reformat my file system accidentally. 83 */ 84 case 'y': 85 force = 1; 86 break; 87 }ARGEND 88 89 if(argc != 1) 90 usage(); 91 92 vtAttach(); 93 94 fmtinstall('V', scoreFmt); 95 fmtinstall('R', vtErrFmt); 96 fmtinstall('L', labelFmt); 97 98 fd = open(argv[0], ORDWR); 99 if(fd < 0) 100 vtFatal("could not open file: %s: %r", argv[0]); 101 102 buf = vtMemAllocZ(bsize); 103 if(pread(fd, buf, bsize, HeaderOffset) != bsize) 104 vtFatal("could not read fs header block: %r"); 105 106 if(headerUnpack(&h, buf) && !force 107 && !confirm("fs header block already exists; are you sure?")) 108 goto Out; 109 110 if((d = dirfstat(fd)) == nil) 111 vtFatal("dirfstat: %r"); 112 113 if(d->type == 'M' && !force 114 && !confirm("fs file is mounted via devmnt (is not a kernel device); are you sure?")) 115 goto Out; 116 117 partition(fd, bsize, &h); 118 headerPack(&h, buf); 119 if(pwrite(fd, buf, bsize, HeaderOffset) < bsize) 120 vtFatal("could not write fs header: %r"); 121 122 disk = diskAlloc(fd); 123 if(disk == nil) 124 vtFatal("could not open disk: %r"); 125 126 /* zero labels */ 127 memset(buf, 0, bsize); 128 for(bn = 0; bn < diskSize(disk, PartLabel); bn++) 129 blockWrite(PartLabel, bn); 130 131 if(score) 132 root = ventiRoot(host, score); 133 else{ 134 rootMetaInit(&e); 135 root = rootInit(&e); 136 } 137 138 superInit(label, root, vtZeroScore); 139 diskFree(disk); 140 141 if(score == nil) 142 topLevel(argv[0]); 143 144 Out: 145 vtDetach(); 146 exits(0); 147 } 148 149 static u64int 150 fdsize(int fd) 151 { 152 Dir *dir; 153 u64int size; 154 155 dir = dirfstat(fd); 156 if(dir == nil) 157 vtFatal("could not stat file: %r"); 158 size = dir->length; 159 free(dir); 160 return size; 161 } 162 163 static void 164 usage(void) 165 { 166 fprint(2, "usage: %s [-b blocksize] [-h host] [-l label] [-v score] [-y] file\n", argv0); 167 exits("usage"); 168 } 169 170 static void 171 partition(int fd, int bsize, Header *h) 172 { 173 ulong nblock, ndata, nlabel; 174 ulong lpb; 175 176 if(bsize % 512 != 0) 177 sysfatal("block size must be a multiple of 512 bytes"); 178 if(bsize > VtMaxLumpSize) 179 sysfatal("block size must be less than %d", VtMaxLumpSize); 180 181 memset(h, 0, sizeof(*h)); 182 h->blockSize = bsize; 183 184 lpb = bsize/LabelSize; 185 186 nblock = fdsize(fd)/bsize; 187 188 /* sanity check */ 189 if(nblock < (HeaderOffset*10)/bsize) 190 vtFatal("file too small"); 191 192 h->super = (HeaderOffset + 2*bsize)/bsize; 193 h->label = h->super + 1; 194 ndata = ((u64int)lpb)*(nblock - h->label)/(lpb+1); 195 nlabel = (ndata + lpb - 1)/lpb; 196 h->data = h->label + nlabel; 197 h->end = h->data + ndata; 198 199 } 200 201 static u32int 202 tagGen(void) 203 { 204 u32int tag; 205 206 for(;;){ 207 tag = lrand(); 208 if(tag > RootTag) 209 break; 210 } 211 return tag; 212 } 213 214 static void 215 entryInit(Entry *e) 216 { 217 e->gen = 0; 218 e->dsize = bsize; 219 e->psize = bsize/VtEntrySize*VtEntrySize; 220 e->flags = VtEntryActive; 221 e->depth = 0; 222 e->size = 0; 223 memmove(e->score, vtZeroScore, VtScoreSize); 224 e->tag = tagGen(); 225 e->snap = 0; 226 e->archive = 0; 227 } 228 229 static void 230 rootMetaInit(Entry *e) 231 { 232 u32int addr; 233 u32int tag; 234 DirEntry de; 235 MetaBlock mb; 236 MetaEntry me; 237 238 memset(&de, 0, sizeof(de)); 239 de.elem = vtStrDup("root"); 240 de.entry = 0; 241 de.gen = 0; 242 de.mentry = 1; 243 de.mgen = 0; 244 de.size = 0; 245 de.qid = qid++; 246 de.uid = vtStrDup("adm"); 247 de.gid = vtStrDup("adm"); 248 de.mid = vtStrDup("adm"); 249 de.mtime = time(0); 250 de.mcount = 0; 251 de.ctime = time(0); 252 de.atime = time(0); 253 de.mode = ModeDir | 0555; 254 255 tag = tagGen(); 256 addr = blockAlloc(BtData, tag); 257 258 /* build up meta block */ 259 memset(buf, 0, bsize); 260 mbInit(&mb, buf, bsize, bsize/100); 261 me.size = deSize(&de); 262 me.p = mbAlloc(&mb, me.size); 263 assert(me.p != nil); 264 dePack(&de, &me); 265 mbInsert(&mb, 0, &me); 266 mbPack(&mb); 267 blockWrite(PartData, addr); 268 deCleanup(&de); 269 270 /* build up entry for meta block */ 271 entryInit(e); 272 e->flags |= VtEntryLocal; 273 e->size = bsize; 274 e->tag = tag; 275 localToGlobal(addr, e->score); 276 } 277 278 static u32int 279 rootInit(Entry *e) 280 { 281 ulong addr; 282 u32int tag; 283 284 tag = tagGen(); 285 286 addr = blockAlloc(BtDir, tag); 287 memset(buf, 0, bsize); 288 289 /* root meta data is in the third entry */ 290 entryPack(e, buf, 2); 291 292 entryInit(e); 293 e->flags |= VtEntryDir; 294 entryPack(e, buf, 0); 295 296 entryInit(e); 297 entryPack(e, buf, 1); 298 299 blockWrite(PartData, addr); 300 301 entryInit(e); 302 e->flags |= VtEntryLocal|VtEntryDir; 303 e->size = VtEntrySize*3; 304 e->tag = tag; 305 localToGlobal(addr, e->score); 306 307 addr = blockAlloc(BtDir, RootTag); 308 memset(buf, 0, bsize); 309 entryPack(e, buf, 0); 310 311 blockWrite(PartData, addr); 312 313 return addr; 314 } 315 316 317 static u32int 318 blockAlloc(int type, u32int tag) 319 { 320 static u32int addr; 321 Label l; 322 int lpb; 323 324 lpb = bsize/LabelSize; 325 326 blockRead(PartLabel, addr/lpb); 327 if(!labelUnpack(&l, buf, addr % lpb) || l.state != BsFree) 328 vtFatal("bad label: %r"); 329 l.epoch = 1; 330 l.epochClose = ~(u32int)0; 331 l.type = type; 332 l.state = BsAlloc; 333 l.tag = tag; 334 labelPack(&l, buf, addr % lpb); 335 blockWrite(PartLabel, addr/lpb); 336 return addr++; 337 } 338 339 static void 340 superInit(char *label, u32int root, uchar score[VtScoreSize]) 341 { 342 Super s; 343 344 memset(buf, 0, bsize); 345 memset(&s, 0, sizeof(s)); 346 s.version = SuperVersion; 347 s.epochLow = 1; 348 s.epochHigh = 1; 349 s.qid = qid; 350 s.active = root; 351 s.next = NilBlock; 352 s.current = NilBlock; 353 strecpy(s.name, s.name+sizeof(s.name), label); 354 memmove(s.last, score, VtScoreSize); 355 356 superPack(&s, buf); 357 blockWrite(PartSuper, 0); 358 } 359 360 static u64int 361 unittoull(char *s) 362 { 363 char *es; 364 u64int n; 365 366 if(s == nil) 367 return TWID64; 368 n = strtoul(s, &es, 0); 369 if(*es == 'k' || *es == 'K'){ 370 n *= 1024; 371 es++; 372 }else if(*es == 'm' || *es == 'M'){ 373 n *= 1024*1024; 374 es++; 375 }else if(*es == 'g' || *es == 'G'){ 376 n *= 1024*1024*1024; 377 es++; 378 } 379 if(*es != '\0') 380 return TWID64; 381 return n; 382 } 383 384 static void 385 blockRead(int part, u32int addr) 386 { 387 if(!diskReadRaw(disk, part, addr, buf)) 388 vtFatal("read failed: %r"); 389 } 390 391 static void 392 blockWrite(int part, u32int addr) 393 { 394 if(!diskWriteRaw(disk, part, addr, buf)) 395 vtFatal("write failed: %r"); 396 } 397 398 static void 399 addFile(File *root, char *name, uint mode) 400 { 401 File *f; 402 403 f = fileCreate(root, name, mode | ModeDir, "adm"); 404 if(f == nil) 405 vtFatal("could not create file: %s: %r", name); 406 fileDecRef(f); 407 } 408 409 static void 410 topLevel(char *name) 411 { 412 Fs *fs; 413 File *root; 414 415 /* ok, now we can open as a fs */ 416 fs = fsOpen(name, z, 100, OReadWrite); 417 if(fs == nil) 418 vtFatal("could not open file system: %r"); 419 vtRLock(fs->elk); 420 root = fsGetRoot(fs); 421 if(root == nil) 422 vtFatal("could not open root: %r"); 423 addFile(root, "active", 0777); /* BUG: add create command to Ccmd instead */ 424 addFile(root, "archive", 0555); 425 addFile(root, "snapshot", 0555); 426 fileDecRef(root); 427 vtRUnlock(fs->elk); 428 fsClose(fs); 429 } 430 431 static int 432 ventiRead(uchar score[VtScoreSize], int type) 433 { 434 int n; 435 436 n = vtRead(z, score, type, buf, bsize); 437 if(n < 0) 438 vtFatal("ventiRead %V (%d) failed: %R", score, type); 439 vtZeroExtend(type, buf, n, bsize); 440 return n; 441 } 442 443 static u32int 444 ventiRoot(char *host, char *s) 445 { 446 int i, n; 447 uchar score[VtScoreSize]; 448 u32int addr, tag; 449 DirEntry de; 450 MetaBlock mb; 451 MetaEntry me; 452 Entry e; 453 VtRoot root; 454 455 if(!parseScore(score, s)) 456 vtFatal("bad score '%s'", s); 457 458 if((z = vtDial(host, 0)) == nil 459 || !vtConnect(z, nil)) 460 vtFatal("connect to venti: %R"); 461 462 tag = tagGen(); 463 addr = blockAlloc(BtDir, tag); 464 465 ventiRead(score, VtRootType); 466 if(!vtRootUnpack(&root, buf)) 467 vtFatal("corrupted root: vtRootUnpack"); 468 n = ventiRead(root.score, VtDirType); 469 470 /* 471 * Fossil's vac archives start with an extra layer of source, 472 * but vac's don't. 473 */ 474 if(n <= 2*VtEntrySize){ 475 if(!entryUnpack(&e, buf, 0)) 476 vtFatal("bad root: top entry"); 477 n = ventiRead(e.score, VtDirType); 478 } 479 480 /* 481 * There should be three root sources (and nothing else) here. 482 */ 483 for(i=0; i<3; i++){ 484 if(!entryUnpack(&e, buf, i) 485 || !(e.flags&VtEntryActive) 486 || e.psize < 256 487 || e.dsize < 256) 488 vtFatal("bad root: entry %d", i); 489 fprint(2, "%V\n", e.score); 490 } 491 if(n > 3*VtEntrySize) 492 vtFatal("bad root: entry count"); 493 494 blockWrite(PartData, addr); 495 496 /* 497 * Maximum qid is recorded in root's msource, entry #2 (conveniently in e). 498 */ 499 ventiRead(e.score, VtDataType); 500 if(!mbUnpack(&mb, buf, bsize)) 501 vtFatal("bad root: mbUnpack"); 502 meUnpack(&me, &mb, 0); 503 if(!deUnpack(&de, &me)) 504 vtFatal("bad root: dirUnpack"); 505 if(!de.qidSpace) 506 vtFatal("bad root: no qidSpace"); 507 qid = de.qidMax; 508 509 /* 510 * Recreate the top layer of source. 511 */ 512 entryInit(&e); 513 e.flags |= VtEntryLocal|VtEntryDir; 514 e.size = VtEntrySize*3; 515 e.tag = tag; 516 localToGlobal(addr, e.score); 517 518 addr = blockAlloc(BtDir, RootTag); 519 memset(buf, 0, bsize); 520 entryPack(&e, buf, 0); 521 blockWrite(PartData, addr); 522 523 return addr; 524 } 525 526 static int 527 parseScore(uchar *score, char *buf) 528 { 529 int i, c; 530 531 memset(score, 0, VtScoreSize); 532 533 if(strlen(buf) < VtScoreSize*2) 534 return 0; 535 for(i=0; i<VtScoreSize*2; i++){ 536 if(buf[i] >= '0' && buf[i] <= '9') 537 c = buf[i] - '0'; 538 else if(buf[i] >= 'a' && buf[i] <= 'f') 539 c = buf[i] - 'a' + 10; 540 else if(buf[i] >= 'A' && buf[i] <= 'F') 541 c = buf[i] - 'A' + 10; 542 else 543 return 0; 544 545 if((i & 1) == 0) 546 c <<= 4; 547 548 score[i>>1] |= c; 549 } 550 return 1; 551 } 552 553