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