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