xref: /plan9/sys/src/cmd/fossil/flfmt.c (revision 2651f6bb1dce6979361f5f2d230af377da2d5a92)
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