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
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
threadmain(int argc,char * argv[])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
fdsize(int fd)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
usage(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
partition(int fd,int bsize,Header * h)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
tagGen(void)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
entryInit(Entry * e)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
rootMetaInit(Entry * e)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
rootInit(Entry * e)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
blockAlloc(int type,u32int tag)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
superInit(char * label,u32int root,uchar score[VtScoreSize])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
unittoull(char * s)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
blockRead(int part,u32int addr)399 blockRead(int part, u32int addr)
400 {
401 if(!diskReadRaw(disk, part, addr, buf))
402 sysfatal("read failed: %r");
403 }
404
405 static void
blockWrite(int part,u32int addr)406 blockWrite(int part, u32int addr)
407 {
408 if(!diskWriteRaw(disk, part, addr, buf))
409 sysfatal("write failed: %r");
410 }
411
412 static void
addFile(File * root,char * name,uint mode)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
topLevel(char * name)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
ventiRead(uchar score[VtScoreSize],int type)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
ventiRoot(char * host,char * s)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
parseScore(uchar * score,char * buf)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 }
568