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