aboutsummaryrefslogtreecommitdiff
path: root/src/libventi/entry.c
blob: 12fb9589f140416c2256f5766765b04e174477a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <u.h>
#include <libc.h>
#include <venti.h>
#include "cvt.h"

static int
checksize(int n)
{
	if(n < 256) {
		werrstr("bad block size %#ux", n);
		return -1;
	}
	return 0;
}

// _VtEntryBig integer format is floating-point:
// (n>>5) << (n&31).
// Convert this number; must be exact or return -1.
int
vttobig(ulong n)
{
	int shift;
	ulong n0;

	n0 = n;
	shift = 0;
	while(n >= (1<<(16 - 5))) {
		if(n & 1)
			return -1;
		shift++;
		n >>= 1;
	}

	n = (n<<5) | shift;
	if(((n>>5)<<(n&31)) != n0)
		sysfatal("vttobig %#lux => %#lux failed", n0, n);
	return n;
}

void
vtentrypack(VtEntry *e, uchar *p, int index)
{
	ulong t32;
	int flags;
	uchar *op;
	int depth;
	int psize, dsize;

	p += index * VtEntrySize;
	op = p;

	depth = e->type&VtTypeDepthMask;
	flags = (e->flags&~(_VtEntryDir|_VtEntryDepthMask));
	flags |= depth << _VtEntryDepthShift;
	if(e->type - depth == VtDirType)
		flags |= _VtEntryDir;
	U32PUT(p, e->gen);
	p += 4;
	psize = e->psize;
	dsize = e->dsize;
	if(psize >= (1<<16) || dsize >= (1<<16)) {
		flags |= _VtEntryBig;
		psize = vttobig(psize);
		dsize = vttobig(dsize);
		if(psize < 0 || dsize < 0)
			sysfatal("invalid entry psize/dsize: %ld/%ld", e->psize, e->dsize);
	}
	U16PUT(p, psize);
	p += 2;
	U16PUT(p, dsize);
	p += 2;
	U8PUT(p, flags);
	p++;
	memset(p, 0, 5);
	p += 5;
	U48PUT(p, e->size, t32);
	p += 6;
	memmove(p, e->score, VtScoreSize);
	p += VtScoreSize;

	assert(p-op == VtEntrySize);
}

int
vtentryunpack(VtEntry *e, uchar *p, int index)
{
	uchar *op;

	p += index * VtEntrySize;
	op = p;

	e->gen = U32GET(p);
	p += 4;
	e->psize = U16GET(p);
	p += 2;
	e->dsize = U16GET(p);
	p += 2;
	e->flags = U8GET(p);
	p++;
	if(e->flags & _VtEntryBig) {
		e->psize = (e->psize>>5)<<(e->psize & 31);
		e->dsize = (e->dsize>>5)<<(e->dsize & 31);
	}
	e->type = (e->flags&_VtEntryDir) ? VtDirType : VtDataType;
	e->type += (e->flags & _VtEntryDepthMask) >> _VtEntryDepthShift;
	e->flags &= ~(_VtEntryDir|_VtEntryDepthMask|_VtEntryBig);
	p += 5;
	e->size = U48GET(p);
	p += 6;
	memmove(e->score, p, VtScoreSize);
	p += VtScoreSize;

	assert(p-op == VtEntrySize);

	if(!(e->flags & VtEntryActive))
		return 0;

	/*
	 * Some old vac files use psize==0 and dsize==0 when the
	 * file itself has size 0 or is zeros.  Just to make programs not
	 * have to figure out what block sizes of 0 means, rewrite them.
	 */
	if(e->psize == 0 && e->dsize == 0
	&& memcmp(e->score, vtzeroscore, VtScoreSize) == 0){
		e->psize = 4096;
		e->dsize = 4096;
	}
	if(checksize(e->psize) < 0 || checksize(e->dsize) < 0)
		return -1;

	return 0;
}