xref: /plan9/sys/src/cmd/fossil/flfmt.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
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, 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 
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, vtZeroScore);
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 }
200 
201 static u32int
202 tagGen(void)
203 {
204 	u32int tag;
205 
206 	for(;;){
207 		tag = lrand();
208 		if(tag > RootTag)
209 			break;
210 	}
211 	return tag;
212 }
213 
214 static void
215 entryInit(Entry *e)
216 {
217 	e->gen = 0;
218 	e->dsize = bsize;
219 	e->psize = bsize/VtEntrySize*VtEntrySize;
220 	e->flags = VtEntryActive;
221 	e->depth = 0;
222 	e->size = 0;
223 	memmove(e->score, vtZeroScore, VtScoreSize);
224 	e->tag = tagGen();
225 	e->snap = 0;
226 	e->archive = 0;
227 }
228 
229 static void
230 rootMetaInit(Entry *e)
231 {
232 	u32int addr;
233 	u32int tag;
234 	DirEntry de;
235 	MetaBlock mb;
236 	MetaEntry me;
237 
238 	memset(&de, 0, sizeof(de));
239 	de.elem = vtStrDup("root");
240 	de.entry = 0;
241 	de.gen = 0;
242 	de.mentry = 1;
243 	de.mgen = 0;
244 	de.size = 0;
245 	de.qid = qid++;
246 	de.uid = vtStrDup("adm");
247 	de.gid = vtStrDup("adm");
248 	de.mid = vtStrDup("adm");
249 	de.mtime = time(0);
250 	de.mcount = 0;
251 	de.ctime = time(0);
252 	de.atime = time(0);
253 	de.mode = ModeDir | 0555;
254 
255 	tag = tagGen();
256 	addr = blockAlloc(BtData, tag);
257 
258 	/* build up meta block */
259 	memset(buf, 0, bsize);
260 	mbInit(&mb, buf, bsize, bsize/100);
261 	me.size = deSize(&de);
262 	me.p = mbAlloc(&mb, me.size);
263 	assert(me.p != nil);
264 	dePack(&de, &me);
265 	mbInsert(&mb, 0, &me);
266 	mbPack(&mb);
267 	blockWrite(PartData, addr);
268 	deCleanup(&de);
269 
270 	/* build up entry for meta block */
271 	entryInit(e);
272 	e->flags |= VtEntryLocal;
273  	e->size = bsize;
274 	e->tag = tag;
275 	localToGlobal(addr, e->score);
276 }
277 
278 static u32int
279 rootInit(Entry *e)
280 {
281 	ulong addr;
282 	u32int tag;
283 
284 	tag = tagGen();
285 
286 	addr = blockAlloc(BtDir, tag);
287 	memset(buf, 0, bsize);
288 
289 	/* root meta data is in the third entry */
290 	entryPack(e, buf, 2);
291 
292 	entryInit(e);
293 	e->flags |= VtEntryDir;
294 	entryPack(e, buf, 0);
295 
296 	entryInit(e);
297 	entryPack(e, buf, 1);
298 
299 	blockWrite(PartData, addr);
300 
301 	entryInit(e);
302 	e->flags |= VtEntryLocal|VtEntryDir;
303  	e->size = VtEntrySize*3;
304 	e->tag = tag;
305 	localToGlobal(addr, e->score);
306 
307 	addr = blockAlloc(BtDir, RootTag);
308 	memset(buf, 0, bsize);
309 	entryPack(e, buf, 0);
310 
311 	blockWrite(PartData, addr);
312 
313 	return addr;
314 }
315 
316 
317 static u32int
318 blockAlloc(int type, u32int tag)
319 {
320 	static u32int addr;
321 	Label l;
322 	int lpb;
323 
324 	lpb = bsize/LabelSize;
325 
326 	blockRead(PartLabel, addr/lpb);
327 	if(!labelUnpack(&l, buf, addr % lpb) || l.state != BsFree)
328 		vtFatal("bad label: %r");
329 	l.epoch = 1;
330 	l.epochClose = ~(u32int)0;
331 	l.type = type;
332 	l.state = BsAlloc;
333 	l.tag = tag;
334 	labelPack(&l, buf, addr % lpb);
335 	blockWrite(PartLabel, addr/lpb);
336 	return addr++;
337 }
338 
339 static void
340 superInit(char *label, u32int root, uchar score[VtScoreSize])
341 {
342 	Super s;
343 
344 	memset(buf, 0, bsize);
345 	memset(&s, 0, sizeof(s));
346 	s.version = SuperVersion;
347 	s.epochLow = 1;
348 	s.epochHigh = 1;
349 	s.qid = qid;
350 	s.active = root;
351 	s.next = NilBlock;
352 	s.current = NilBlock;
353 	strecpy(s.name, s.name+sizeof(s.name), label);
354 	memmove(s.last, score, VtScoreSize);
355 
356 	superPack(&s, buf);
357 	blockWrite(PartSuper, 0);
358 }
359 
360 static u64int
361 unittoull(char *s)
362 {
363 	char *es;
364 	u64int n;
365 
366 	if(s == nil)
367 		return TWID64;
368 	n = strtoul(s, &es, 0);
369 	if(*es == 'k' || *es == 'K'){
370 		n *= 1024;
371 		es++;
372 	}else if(*es == 'm' || *es == 'M'){
373 		n *= 1024*1024;
374 		es++;
375 	}else if(*es == 'g' || *es == 'G'){
376 		n *= 1024*1024*1024;
377 		es++;
378 	}
379 	if(*es != '\0')
380 		return TWID64;
381 	return n;
382 }
383 
384 static void
385 blockRead(int part, u32int addr)
386 {
387 	if(!diskReadRaw(disk, part, addr, buf))
388 		vtFatal("read failed: %r");
389 }
390 
391 static void
392 blockWrite(int part, u32int addr)
393 {
394 	if(!diskWriteRaw(disk, part, addr, buf))
395 		vtFatal("write failed: %r");
396 }
397 
398 static void
399 addFile(File *root, char *name, uint mode)
400 {
401 	File *f;
402 
403 	f = fileCreate(root, name, mode | ModeDir, "adm");
404 	if(f == nil)
405 		vtFatal("could not create file: %s: %r", name);
406 	fileDecRef(f);
407 }
408 
409 static void
410 topLevel(char *name)
411 {
412 	Fs *fs;
413 	File *root;
414 
415 	/* ok, now we can open as a fs */
416 	fs = fsOpen(name, z, 100, OReadWrite);
417 	if(fs == nil)
418 		vtFatal("could not open file system: %r");
419 	vtRLock(fs->elk);
420 	root = fsGetRoot(fs);
421 	if(root == nil)
422 		vtFatal("could not open root: %r");
423 	addFile(root, "active", 0777);	/* BUG: add create command to Ccmd instead */
424 	addFile(root, "archive", 0555);
425 	addFile(root, "snapshot", 0555);
426 	fileDecRef(root);
427 	vtRUnlock(fs->elk);
428 	fsClose(fs);
429 }
430 
431 static int
432 ventiRead(uchar score[VtScoreSize], int type)
433 {
434 	int n;
435 
436 	n = vtRead(z, score, type, buf, bsize);
437 	if(n < 0)
438 		vtFatal("ventiRead %V (%d) failed: %R", score, type);
439 	vtZeroExtend(type, buf, n, bsize);
440 	return n;
441 }
442 
443 static u32int
444 ventiRoot(char *host, char *s)
445 {
446 	int i, n;
447 	uchar score[VtScoreSize];
448 	u32int addr, tag;
449 	DirEntry de;
450 	MetaBlock mb;
451 	MetaEntry me;
452 	Entry e;
453 	VtRoot root;
454 
455 	if(!parseScore(score, s))
456 		vtFatal("bad score '%s'", s);
457 
458 	if((z = vtDial(host, 0)) == nil
459 	|| !vtConnect(z, nil))
460 		vtFatal("connect to venti: %R");
461 
462 	tag = tagGen();
463 	addr = blockAlloc(BtDir, tag);
464 
465 	ventiRead(score, VtRootType);
466 	if(!vtRootUnpack(&root, buf))
467 		vtFatal("corrupted root: vtRootUnpack");
468 	n = ventiRead(root.score, VtDirType);
469 
470 	/*
471 	 * Fossil's vac archives start with an extra layer of source,
472 	 * but vac's don't.
473 	 */
474 	if(n <= 2*VtEntrySize){
475 		if(!entryUnpack(&e, buf, 0))
476 			vtFatal("bad root: top entry");
477 		n = ventiRead(e.score, VtDirType);
478 	}
479 
480 	/*
481 	 * There should be three root sources (and nothing else) here.
482 	 */
483 	for(i=0; i<3; i++){
484 		if(!entryUnpack(&e, buf, i)
485 		|| !(e.flags&VtEntryActive)
486 		|| e.psize < 256
487 		|| e.dsize < 256)
488 			vtFatal("bad root: entry %d", i);
489 		fprint(2, "%V\n", e.score);
490 	}
491 	if(n > 3*VtEntrySize)
492 		vtFatal("bad root: entry count");
493 
494 	blockWrite(PartData, addr);
495 
496 	/*
497 	 * Maximum qid is recorded in root's msource, entry #2 (conveniently in e).
498 	 */
499 	ventiRead(e.score, VtDataType);
500 	if(!mbUnpack(&mb, buf, bsize))
501 		vtFatal("bad root: mbUnpack");
502 	meUnpack(&me, &mb, 0);
503 	if(!deUnpack(&de, &me))
504 		vtFatal("bad root: dirUnpack");
505 	if(!de.qidSpace)
506 		vtFatal("bad root: no qidSpace");
507 	qid = de.qidMax;
508 
509 	/*
510 	 * Recreate the top layer of source.
511 	 */
512 	entryInit(&e);
513 	e.flags |= VtEntryLocal|VtEntryDir;
514 	e.size = VtEntrySize*3;
515 	e.tag = tag;
516 	localToGlobal(addr, e.score);
517 
518 	addr = blockAlloc(BtDir, RootTag);
519 	memset(buf, 0, bsize);
520 	entryPack(&e, buf, 0);
521 	blockWrite(PartData, addr);
522 
523 	return addr;
524 }
525 
526 static int
527 parseScore(uchar *score, char *buf)
528 {
529 	int i, c;
530 
531 	memset(score, 0, VtScoreSize);
532 
533 	if(strlen(buf) < VtScoreSize*2)
534 		return 0;
535 	for(i=0; i<VtScoreSize*2; i++){
536 		if(buf[i] >= '0' && buf[i] <= '9')
537 			c = buf[i] - '0';
538 		else if(buf[i] >= 'a' && buf[i] <= 'f')
539 			c = buf[i] - 'a' + 10;
540 		else if(buf[i] >= 'A' && buf[i] <= 'F')
541 			c = buf[i] - 'A' + 10;
542 		else
543 			return 0;
544 
545 		if((i & 1) == 0)
546 			c <<= 4;
547 
548 		score[i>>1] |= c;
549 	}
550 	return 1;
551 }
552 
553