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