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