xref: /plan9/sys/src/cmd/fossil/vac.c (revision 6042bf6d66dcc60b2e35639f4cefa24e2aa2ecb8)
1 #include "stdinc.h"
2 
3 typedef struct MetaChunk MetaChunk;
4 
5 struct MetaChunk {
6 	ushort offset;
7 	ushort size;
8 	ushort index;
9 };
10 
11 static int stringUnpack(char **s, uchar **p, int *n);
12 static int meCmp(MetaEntry*, char *s);
13 static int meCmpOld(MetaEntry*, char *s);
14 
15 
16 
17 static char EBadMeta[] = "corrupted meta data";
18 static char ENoFile[] = "file does not exist";
19 
20 /*
21  * integer conversion routines
22  */
23 #define	U8GET(p)	((p)[0])
24 #define	U16GET(p)	(((p)[0]<<8)|(p)[1])
25 #define	U32GET(p)	(((p)[0]<<24)|((p)[1]<<16)|((p)[2]<<8)|(p)[3])
26 #define	U48GET(p)	(((uvlong)U16GET(p)<<32)|(uvlong)U32GET((p)+2))
27 #define	U64GET(p)	(((uvlong)U32GET(p)<<32)|(uvlong)U32GET((p)+4))
28 
29 #define	U8PUT(p,v)	(p)[0]=(v)
30 #define	U16PUT(p,v)	(p)[0]=(v)>>8;(p)[1]=(v)
31 #define	U32PUT(p,v)	(p)[0]=(v)>>24;(p)[1]=(v)>>16;(p)[2]=(v)>>8;(p)[3]=(v)
32 #define	U48PUT(p,v,t32)	t32=(v)>>32;U16PUT(p,t32);t32=(v);U32PUT((p)+2,t32)
33 #define	U64PUT(p,v,t32)	t32=(v)>>32;U32PUT(p,t32);t32=(v);U32PUT((p)+4,t32)
34 
35 static int
stringUnpack(char ** s,uchar ** p,int * n)36 stringUnpack(char **s, uchar **p, int *n)
37 {
38 	int nn;
39 
40 	if(*n < 2)
41 		return 0;
42 
43 	nn = U16GET(*p);
44 	*p += 2;
45 	*n -= 2;
46 	if(nn > *n)
47 		return 0;
48 	*s = vtMemAlloc(nn+1);
49 	memmove(*s, *p, nn);
50 	(*s)[nn] = 0;
51 	*p += nn;
52 	*n -= nn;
53 	return 1;
54 }
55 
56 static int
stringPack(char * s,uchar * p)57 stringPack(char *s, uchar *p)
58 {
59 	int n;
60 
61 	n = strlen(s);
62 	U16PUT(p, n);
63 	memmove(p+2, s, n);
64 	return n+2;
65 }
66 
67 int
mbSearch(MetaBlock * mb,char * elem,int * ri,MetaEntry * me)68 mbSearch(MetaBlock *mb, char *elem, int *ri, MetaEntry *me)
69 {
70 	int i;
71 	int b, t, x;
72 if(0)fprint(2, "mbSearch %s\n", elem);
73 
74 	/* binary search within block */
75 	b = 0;
76 	t = mb->nindex;
77 	while(b < t){
78 		i = (b+t)>>1;
79 		meUnpack(me, mb, i);
80 
81 		if(mb->botch)
82 			x = meCmpOld(me, elem);
83 		else
84 			x = meCmp(me, elem);
85 
86 		if(x == 0){
87 			*ri = i;
88 			return 1;
89 		}
90 
91 		if(x < 0)
92 			b = i+1;
93 		else /* x > 0 */
94 			t = i;
95 	}
96 
97 	assert(b == t);
98 
99 	*ri = b;	/* b is the index to insert this entry */
100 	memset(me, 0, sizeof(*me));
101 
102 	vtSetError(ENoFile);
103 	return 0;
104 }
105 
106 void
mbInit(MetaBlock * mb,uchar * p,int n,int ne)107 mbInit(MetaBlock *mb, uchar *p, int n, int ne)
108 {
109 	memset(p, 0, n);
110 	mb->maxsize = n;
111 	mb->maxindex = ne;
112 	mb->nindex = 0;
113 	mb->free = 0;
114 	mb->size = MetaHeaderSize + ne*MetaIndexSize;
115 	mb->buf = p;
116 	mb->botch = 0;
117 }
118 
119 int
mbUnpack(MetaBlock * mb,uchar * p,int n)120 mbUnpack(MetaBlock *mb, uchar *p, int n)
121 {
122 	u32int magic;
123 	int i;
124 	int eo, en, omin;
125 	uchar *q;
126 
127 	mb->maxsize = n;
128 	mb->buf = p;
129 
130 	if(n == 0){
131 		memset(mb, 0, sizeof(MetaBlock));
132 		return 1;
133 	}
134 
135 	magic = U32GET(p);
136 	if(magic != MetaMagic && magic != MetaMagic-1)
137 		goto Err;
138 	mb->size = U16GET(p+4);
139 	mb->free = U16GET(p+6);
140 	mb->maxindex = U16GET(p+8);
141 	mb->nindex = U16GET(p+10);
142 	mb->botch = magic != MetaMagic;
143 	if(mb->size > n)
144 		goto Err;
145 
146 	omin = MetaHeaderSize + mb->maxindex*MetaIndexSize;
147 	if(n < omin)
148 		goto Err;
149 
150 
151 	p += MetaHeaderSize;
152 
153 	/* check the index table - ensures that meUnpack and meCmp never fail */
154 	for(i=0; i<mb->nindex; i++){
155 		eo = U16GET(p);
156 		en = U16GET(p+2);
157 		if(eo < omin || eo+en > mb->size || en < 8)
158 			goto Err;
159 		q = mb->buf + eo;
160 		if(U32GET(q) != DirMagic)
161 			goto Err;
162 		p += 4;
163 	}
164 
165 	return 1;
166 Err:
167 	vtSetError(EBadMeta);
168 	return 0;
169 }
170 
171 
172 void
mbPack(MetaBlock * mb)173 mbPack(MetaBlock *mb)
174 {
175 	uchar *p;
176 
177 	p = mb->buf;
178 
179 	assert(!mb->botch);
180 
181 	U32PUT(p, MetaMagic);
182 	U16PUT(p+4, mb->size);
183 	U16PUT(p+6, mb->free);
184 	U16PUT(p+8, mb->maxindex);
185 	U16PUT(p+10, mb->nindex);
186 }
187 
188 
189 void
mbDelete(MetaBlock * mb,int i)190 mbDelete(MetaBlock *mb, int i)
191 {
192 	uchar *p;
193 	int n;
194 	MetaEntry me;
195 
196 	assert(i < mb->nindex);
197 	meUnpack(&me, mb, i);
198 	memset(me.p, 0, me.size);
199 
200 	if(me.p - mb->buf + me.size == mb->size)
201 		mb->size -= me.size;
202 	else
203 		mb->free += me.size;
204 
205 	p = mb->buf + MetaHeaderSize + i*MetaIndexSize;
206 	n = (mb->nindex-i-1)*MetaIndexSize;
207 	memmove(p, p+MetaIndexSize, n);
208 	memset(p+n, 0, MetaIndexSize);
209 	mb->nindex--;
210 }
211 
212 void
mbInsert(MetaBlock * mb,int i,MetaEntry * me)213 mbInsert(MetaBlock *mb, int i, MetaEntry *me)
214 {
215 	uchar *p;
216 	int o, n;
217 
218 	assert(mb->nindex < mb->maxindex);
219 
220 	o = me->p - mb->buf;
221 	n = me->size;
222 	if(o+n > mb->size){
223 		mb->free -= mb->size - o;
224 		mb->size = o + n;
225 	}else
226 		mb->free -= n;
227 
228 	p = mb->buf + MetaHeaderSize + i*MetaIndexSize;
229 	n = (mb->nindex-i)*MetaIndexSize;
230 	memmove(p+MetaIndexSize, p, n);
231 	U16PUT(p, me->p - mb->buf);
232 	U16PUT(p+2, me->size);
233 	mb->nindex++;
234 }
235 
236 int
mbResize(MetaBlock * mb,MetaEntry * me,int n)237 mbResize(MetaBlock *mb, MetaEntry *me, int n)
238 {
239 	uchar *p, *ep;
240 
241 	/* easy case */
242 	if(n <= me->size){
243 		me->size = n;
244 		return 1;
245 	}
246 
247 	/* try and expand entry */
248 
249 	p = me->p + me->size;
250 	ep = mb->buf + mb->maxsize;
251 	while(p < ep && *p == 0)
252 		p++;
253 	if(n <= p - me->p){
254 		me->size = n;
255 		return 1;
256 	}
257 
258 	p = mbAlloc(mb, n);
259 	if(p != nil){
260 		me->p = p;
261 		me->size = n;
262 		return 1;
263 	}
264 
265 	return 0;
266 }
267 
268 void
meUnpack(MetaEntry * me,MetaBlock * mb,int i)269 meUnpack(MetaEntry *me, MetaBlock *mb, int i)
270 {
271 	uchar *p;
272 	int eo, en;
273 
274 	assert(i >= 0 && i < mb->nindex);
275 
276 	p = mb->buf + MetaHeaderSize + i*MetaIndexSize;
277 	eo = U16GET(p);
278 	en = U16GET(p+2);
279 
280 	me->p = mb->buf + eo;
281 	me->size = en;
282 
283 	/* checked by mbUnpack */
284 	assert(me->size >= 8);
285 }
286 
287 /* assumes a small amount of checking has been done in mbEntry */
288 static int
meCmp(MetaEntry * me,char * s)289 meCmp(MetaEntry *me, char *s)
290 {
291 	int n;
292 	uchar *p;
293 
294 	p = me->p;
295 
296 	/* skip magic & version */
297 	p += 6;
298 	n = U16GET(p);
299 	p += 2;
300 
301 	if(n > me->size - 8)
302 		n = me->size - 8;
303 
304 	while(n > 0){
305 		if(*s == 0)
306 			return 1;
307 		if(*p < (uchar)*s)
308 			return -1;
309 		if(*p > (uchar)*s)
310 			return 1;
311 		p++;
312 		s++;
313 		n--;
314 	}
315 	return -(*s != 0);
316 }
317 
318 /*
319  * This is the old and broken meCmp.
320  * This cmp routine reverse the sense of the comparison
321  * when one string is a prefix of the other.
322  * In other words, it put "ab" after "abc" rather
323  * than before.  This behaviour is ok; binary search
324  * and sort still work.  However, it is goes against
325  * the usual convention.
326  */
327 static int
meCmpOld(MetaEntry * me,char * s)328 meCmpOld(MetaEntry *me, char *s)
329 {
330 	int n;
331 	uchar *p;
332 
333 	p = me->p;
334 
335 	/* skip magic & version */
336 	p += 6;
337 	n = U16GET(p);
338 	p += 2;
339 
340 	if(n > me->size - 8)
341 		n = me->size - 8;
342 
343 	while(n > 0){
344 		if(*s == 0)
345 			return -1;
346 		if(*p < (uchar)*s)
347 			return -1;
348 		if(*p > (uchar)*s)
349 			return 1;
350 		p++;
351 		s++;
352 		n--;
353 	}
354 	return *s != 0;
355 }
356 
357 static int
offsetCmp(void * s0,void * s1)358 offsetCmp(void *s0, void *s1)
359 {
360 	MetaChunk *mc0, *mc1;
361 
362 	mc0 = s0;
363 	mc1 = s1;
364 	if(mc0->offset < mc1->offset)
365 		return -1;
366 	if(mc0->offset > mc1->offset)
367 		return 1;
368 	return 0;
369 }
370 
371 static MetaChunk *
metaChunks(MetaBlock * mb)372 metaChunks(MetaBlock *mb)
373 {
374 	MetaChunk *mc;
375 	int oo, o, n, i;
376 	uchar *p;
377 
378 	mc = vtMemAlloc(mb->nindex*sizeof(MetaChunk));
379 	p = mb->buf + MetaHeaderSize;
380 	for(i = 0; i<mb->nindex; i++){
381 		mc[i].offset = U16GET(p);
382 		mc[i].size = U16GET(p+2);
383 		mc[i].index = i;
384 		p += MetaIndexSize;
385 	}
386 
387 	qsort(mc, mb->nindex, sizeof(MetaChunk), offsetCmp);
388 
389 	/* check block looks ok */
390 	oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
391 	o = oo;
392 	n = 0;
393 	for(i=0; i<mb->nindex; i++){
394 		o = mc[i].offset;
395 		n = mc[i].size;
396 		if(o < oo)
397 			goto Err;
398 		oo += n;
399 	}
400 	if(o+n > mb->size)
401 		goto Err;
402 	if(mb->size - oo != mb->free)
403 		goto Err;
404 
405 	return mc;
406 Err:
407 fprint(2, "metaChunks failed!\n");
408 oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
409 for(i=0; i<mb->nindex; i++){
410 fprint(2, "\t%d: %d %d\n", i, mc[i].offset, mc[i].offset + mc[i].size);
411 oo += mc[i].size;
412 }
413 fprint(2, "\tused=%d size=%d free=%d free2=%d\n", oo, mb->size, mb->free, mb->size - oo);
414 	vtSetError(EBadMeta);
415 	vtMemFree(mc);
416 	return nil;
417 }
418 
419 static void
mbCompact(MetaBlock * mb,MetaChunk * mc)420 mbCompact(MetaBlock *mb, MetaChunk *mc)
421 {
422 	int oo, o, n, i;
423 
424 	oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
425 
426 	for(i=0; i<mb->nindex; i++){
427 		o = mc[i].offset;
428 		n = mc[i].size;
429 		if(o != oo){
430 			memmove(mb->buf + oo, mb->buf + o, n);
431 			U16PUT(mb->buf + MetaHeaderSize + mc[i].index*MetaIndexSize, oo);
432 		}
433 		oo += n;
434 	}
435 
436 	mb->size = oo;
437 	mb->free = 0;
438 }
439 
440 uchar *
mbAlloc(MetaBlock * mb,int n)441 mbAlloc(MetaBlock *mb, int n)
442 {
443 	int i, o;
444 	MetaChunk *mc;
445 
446 	/* off the end */
447 	if(mb->maxsize - mb->size >= n)
448 		return mb->buf + mb->size;
449 
450 	/* check if possible */
451 	if(mb->maxsize - mb->size + mb->free < n)
452 		return nil;
453 
454 	mc = metaChunks(mb);
455 	if(mc == nil){
456 fprint(2, "mbAlloc: metaChunks failed: %r\n");
457 		return nil;
458 	}
459 
460 	/* look for hole */
461 	o = MetaHeaderSize + mb->maxindex*MetaIndexSize;
462 	for(i=0; i<mb->nindex; i++){
463 		if(mc[i].offset - o >= n){
464 			vtMemFree(mc);
465 			return mb->buf + o;
466 		}
467 		o = mc[i].offset + mc[i].size;
468 	}
469 
470 	if(mb->maxsize - o >= n){
471 		vtMemFree(mc);
472 		return mb->buf + o;
473 	}
474 
475 	/* compact and return off the end */
476 	mbCompact(mb, mc);
477 	vtMemFree(mc);
478 
479 	if(mb->maxsize - mb->size < n){
480 		vtSetError(EBadMeta);
481 		return nil;
482 	}
483 	return mb->buf + mb->size;
484 }
485 
486 int
deSize(DirEntry * dir)487 deSize(DirEntry *dir)
488 {
489 	int n;
490 
491 	/* constant part */
492 
493 	n = 	4 +	/* magic */
494 		2 + 	/* version */
495 		4 +	/* entry */
496 		4 + 	/* guid */
497 		4 + 	/* mentry */
498 		4 + 	/* mgen */
499 		8 +	/* qid */
500 		4 + 	/* mtime */
501 		4 + 	/* mcount */
502 		4 + 	/* ctime */
503 		4 + 	/* atime */
504 		4 +	/* mode */
505 		0;
506 
507 	/* strings */
508 	n += 2 + strlen(dir->elem);
509 	n += 2 + strlen(dir->uid);
510 	n += 2 + strlen(dir->gid);
511 	n += 2 + strlen(dir->mid);
512 
513 	/* optional sections */
514 	if(dir->qidSpace){
515 		n += 	3 + 	/* option header */
516 			8 + 	/* qidOffset */
517 			8;	/* qid Max */
518 	}
519 
520 	return n;
521 }
522 
523 void
dePack(DirEntry * dir,MetaEntry * me)524 dePack(DirEntry *dir, MetaEntry *me)
525 {
526 	uchar *p;
527 	ulong t32;
528 
529 	p = me->p;
530 
531 	U32PUT(p, DirMagic);
532 	U16PUT(p+4, 9);		/* version */
533 	p += 6;
534 
535 	p += stringPack(dir->elem, p);
536 
537 	U32PUT(p, dir->entry);
538 	U32PUT(p+4, dir->gen);
539 	U32PUT(p+8, dir->mentry);
540 	U32PUT(p+12, dir->mgen);
541 	U64PUT(p+16, dir->qid, t32);
542 	p += 24;
543 
544 	p += stringPack(dir->uid, p);
545 	p += stringPack(dir->gid, p);
546 	p += stringPack(dir->mid, p);
547 
548 	U32PUT(p, dir->mtime);
549 	U32PUT(p+4, dir->mcount);
550 	U32PUT(p+8, dir->ctime);
551 	U32PUT(p+12, dir->atime);
552 	U32PUT(p+16, dir->mode);
553 	p += 5*4;
554 
555 	if(dir->qidSpace){
556 		U8PUT(p, DeQidSpace);
557 		U16PUT(p+1, 2*8);
558 		p += 3;
559 		U64PUT(p, dir->qidOffset, t32);
560 		U64PUT(p+8, dir->qidMax, t32);
561 		p += 16;
562 	}
563 
564 	assert(p == me->p + me->size);
565 }
566 
567 
568 int
deUnpack(DirEntry * dir,MetaEntry * me)569 deUnpack(DirEntry *dir, MetaEntry *me)
570 {
571 	int t, nn, n, version;
572 	uchar *p;
573 
574 	p = me->p;
575 	n = me->size;
576 
577 	memset(dir, 0, sizeof(DirEntry));
578 
579 if(0)print("deUnpack\n");
580 	/* magic */
581 	if(n < 4 || U32GET(p) != DirMagic)
582 		goto Err;
583 	p += 4;
584 	n -= 4;
585 
586 if(0)print("deUnpack: got magic\n");
587 	/* version */
588 	if(n < 2)
589 		goto Err;
590 	version = U16GET(p);
591 	if(version < 7 || version > 9)
592 		goto Err;
593 	p += 2;
594 	n -= 2;
595 
596 if(0)print("deUnpack: got version\n");
597 
598 	/* elem */
599 	if(!stringUnpack(&dir->elem, &p, &n))
600 		goto Err;
601 
602 if(0)print("deUnpack: got elem\n");
603 
604 	/* entry  */
605 	if(n < 4)
606 		goto Err;
607 	dir->entry = U32GET(p);
608 	p += 4;
609 	n -= 4;
610 
611 if(0)print("deUnpack: got entry\n");
612 
613 	if(version < 9){
614 		dir->gen = 0;
615 		dir->mentry = dir->entry+1;
616 		dir->mgen = 0;
617 	}else{
618 		if(n < 3*4)
619 			goto Err;
620 		dir->gen = U32GET(p);
621 		dir->mentry = U32GET(p+4);
622 		dir->mgen = U32GET(p+8);
623 		p += 3*4;
624 		n -= 3*4;
625 	}
626 
627 if(0)print("deUnpack: got gen etc\n");
628 
629 	/* size is gotten from VtEntry */
630 	dir->size = 0;
631 
632 	/* qid */
633 	if(n < 8)
634 		goto Err;
635 	dir->qid = U64GET(p);
636 	p += 8;
637 	n -= 8;
638 
639 if(0)print("deUnpack: got qid\n");
640 	/* skip replacement */
641 	if(version == 7){
642 		if(n < VtScoreSize)
643 			goto Err;
644 		p += VtScoreSize;
645 		n -= VtScoreSize;
646 	}
647 
648 	/* uid */
649 	if(!stringUnpack(&dir->uid, &p, &n))
650 		goto Err;
651 
652 	/* gid */
653 	if(!stringUnpack(&dir->gid, &p, &n))
654 		goto Err;
655 
656 	/* mid */
657 	if(!stringUnpack(&dir->mid, &p, &n))
658 		goto Err;
659 
660 if(0)print("deUnpack: got ids\n");
661 	if(n < 5*4)
662 		goto Err;
663 	dir->mtime = U32GET(p);
664 	dir->mcount = U32GET(p+4);
665 	dir->ctime = U32GET(p+8);
666 	dir->atime = U32GET(p+12);
667 	dir->mode = U32GET(p+16);
668 	p += 5*4;
669 	n -= 5*4;
670 
671 if(0)print("deUnpack: got times\n");
672 	/* optional meta data */
673 	while(n > 0){
674 		if(n < 3)
675 			goto Err;
676 		t = p[0];
677 		nn = U16GET(p+1);
678 		p += 3;
679 		n -= 3;
680 		if(n < nn)
681 			goto Err;
682 		switch(t){
683 		case DePlan9:
684 			/* not valid in version >= 9 */
685 			if(version >= 9)
686 				break;
687 			if(dir->plan9 || nn != 12)
688 				goto Err;
689 			dir->plan9 = 1;
690 			dir->p9path = U64GET(p);
691 			dir->p9version = U32GET(p+8);
692 			if(dir->mcount == 0)
693 				dir->mcount = dir->p9version;
694 			break;
695 		case DeGen:
696 			/* not valid in version >= 9 */
697 			if(version >= 9)
698 				break;
699 			break;
700 		case DeQidSpace:
701 			if(dir->qidSpace || nn != 16)
702 				goto Err;
703 			dir->qidSpace = 1;
704 			dir->qidOffset = U64GET(p);
705 			dir->qidMax = U64GET(p+8);
706 			break;
707 		}
708 		p += nn;
709 		n -= nn;
710 	}
711 if(0)print("deUnpack: got options\n");
712 
713 	if(p != me->p + me->size)
714 		goto Err;
715 
716 if(0)print("deUnpack: correct size\n");
717 	return 1;
718 Err:
719 if(0)print("deUnpack: XXXXXXXXXXXX EBadMeta\n");
720 	vtSetError(EBadMeta);
721 	deCleanup(dir);
722 	return 0;
723 }
724 
725 void
deCleanup(DirEntry * dir)726 deCleanup(DirEntry *dir)
727 {
728 	vtMemFree(dir->elem);
729 	dir->elem = nil;
730 	vtMemFree(dir->uid);
731 	dir->uid = nil;
732 	vtMemFree(dir->gid);
733 	dir->gid = nil;
734 	vtMemFree(dir->mid);
735 	dir->mid = nil;
736 }
737 
738 void
deCopy(DirEntry * dst,DirEntry * src)739 deCopy(DirEntry *dst, DirEntry *src)
740 {
741 	*dst = *src;
742 	dst->elem = vtStrDup(src->elem);
743 	dst->uid = vtStrDup(src->uid);
744 	dst->gid = vtStrDup(src->gid);
745 	dst->mid = vtStrDup(src->mid);
746 }
747