-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtslab.c
226 lines (174 loc) · 5.21 KB
/
tslab.c
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#include "bolo.h"
/* a SLAB endian-check magic number, to be
read/written as a 32-bit unsigned integer.
translates into BE as [hex 7e d1 32 4c]
and LE as [hex 4c 32 d1 7e] */
#define ENDIAN_MAGIC 2127639116U
int tslab_map(struct tslab *s, int fd)
{
CHECK(s != NULL, "tslab_map() given a NULL tslab to map");
CHECK(fd >= 0, "tslab_map() given an invalid file descriptor");
int i, rc;
char header[TSLAB_HEADER_SIZE];
ssize_t nread;
off_t n;
errno = BOLO_EBADSLAB;
nread = read(fd, header, TSLAB_HEADER_SIZE);
if (nread < 0) /* read error! */
return -1;
if (nread != TSLAB_HEADER_SIZE) /* short read! */
return -1;
if (memcmp(header, "SLABv1", 6) != 0) /* not a slab! */
return -1;
/* check the HMAC */
errno = BOLO_EBADHMAC;
if (s->key && hmac_check(s->key->key, s->key->len, header, TSLAB_HEADER_SIZE) != 0)
return -1;
/* check host endianness vs file endianness */
errno = BOLO_EENDIAN;
if (read32(header, 8) != ENDIAN_MAGIC)
return -1;
s->fd = fd;
s->block_size = (1 << read8(header, 6));
s->number = read64(header, 16);
n = lseek(s->fd, 0, SEEK_END);
if (n < 0)
return -1;
n -= 4096;
errno = BOLO_EBADSLAB;
if (n == 0) /* orphaned header */
return -1;
/* scan blocks! */
memset(s->blocks, 0, sizeof(s->blocks));
lseek(s->fd, 4096, SEEK_SET);
for (i = 0; i < TBLOCKS_PER_TSLAB && n > 0; i++, n -= s->block_size) {
s->blocks[i].key = s->key;
rc = tblock_map(&s->blocks[i], s->fd,
4096 + i * s->block_size, /* grab the i'th block */
s->block_size);
if (rc != 0)
return -1;
s->blocks[i].valid = 1;
}
return 0;
}
int tslab_unmap(struct tslab *s)
{
int i, ok;
CHECK(s != NULL, "tslab_unmap() given a NULL tslab");
ok = 0;
for (i = 0; i < TBLOCKS_PER_TSLAB; i++) {
if (!s->blocks[i].valid)
break;
s->blocks[i].valid = 0;
if (tblock_unmap(&s->blocks[i]) != 0)
ok = -1;
}
close(s->fd);
return ok;
}
int tslab_sync(struct tslab *s)
{
int i, ok;
CHECK(s != NULL, "tslab_sync() given a NULL tslab to synchronize");
ok = 0;
for (i = 0; i < TBLOCKS_PER_TSLAB; i++) {
if (!s->blocks[i].valid)
break;
if (tblock_sync(&s->blocks[i]) != 0)
ok = -1;
}
return ok;
}
int tslab_init(struct tslab *s, int fd, uint64_t number, uint32_t block_size)
{
char header[TSLAB_HEADER_SIZE];
size_t nwrit;
CHECK(s != NULL, "tslab_init() given a NULL tslab to initialize");
CHECK(block_size == (1 << 19), "tslab_init() given a non-standard block size");
memset(header, 0, sizeof(header));
memcpy(header, "SLABv1", 6);
write8(header, 6, 19); /* from block_size, ostensibly */
write32(header, 8, ENDIAN_MAGIC);
write64(header, 16, tslab_number(number));
if (s->key)
hmac_seal(s->key->key, s->key->len, header, sizeof(header));
lseek(fd, 0, SEEK_SET);
nwrit = write(fd, header, sizeof(header));
if (nwrit != sizeof(header))
return -1;
/* align to a page boundary */
lseek(fd, sysconf(_SC_PAGESIZE) - 1, SEEK_SET);
if (write(fd, "\0", 1) != 1)
return -1;
s->fd = fd;
s->block_size = block_size;
s->number = number;
memset(s->blocks, 0, sizeof(s->blocks));
return 0;
}
int tslab_isfull(struct tslab *s)
{
int i;
CHECK(s != NULL, "tslab_isfull() given a NULL tslab to query");
for (i = 0; i < TBLOCKS_PER_TSLAB; i++)
if (!s->blocks[i].valid)
return 0; /* this block is avail; slab is not full */
return 1; /* no blocks avail; slab is full */
}
int tslab_extend(struct tslab *s, bolo_msec_t base)
{
int i;
off_t start;
size_t len;
CHECK(s != NULL, "tslab_extend() given a NULL tslab to extend");
/* seek to the end of the fd, so we can extend it */
if (lseek(s->fd, 0, SEEK_END) < 0)
return -1;
/* find the first available (!valid) block */
for (i = 0; i < TBLOCKS_PER_TSLAB; i++) {
if (s->blocks[i].valid)
continue;
len = s->block_size;
start = sysconf(_SC_PAGESIZE) + i * len;
CHECK(len == (1 << 19), "tslab_extend() was told to extend a tslab with a non-standard block size");
/* track the encryption key */
s->blocks[i].key = s->key;
/* extend the file descriptor one TBLOCK's worth */
if (lseek(s->fd, start + len - 1, SEEK_SET) < 0)
return -1;
if (write(s->fd, "\0", 1) != 1)
return -1;
/* map the new block into memory */
if (tblock_map(&s->blocks[i], s->fd, start, len) == 0) {
tblock_init(&s->blocks[i], tslab_number(s->number) | i, base);
return 0;
}
/* map failed; truncate the fd back to previous size */
ftruncate(s->fd, start);
lseek(s->fd, 0, SEEK_END);
/* ... and signal failure */
return -1;
}
/* this slab is full; signal failure */
return -1;
}
struct tblock *
tslab_tblock(struct tslab *s, uint64_t id, bolo_msec_t ts)
{
CHECK(s != NULL, "tslab_tblock() given a NULL tslab to query");
errno = BOLO_ENOBLOCK;
if (tslab_number(id) != s->number)
return NULL;
errno = BOLO_EBLKCONT;
if (!s->blocks[tblock_number(id)].valid) {
/* FIXME: right now, we require the tblock to be next in line */
if (tblock_number(id) > 0
&& !s->blocks[tblock_number(id - 1)].valid)
return NULL;
if (tslab_extend(s, ts) != 0)
return NULL;
CHECK(s->blocks[tblock_number(id)].valid, "tslab_tblock() detected tblock corruption; the desired tblock was not marked as valid after extension");
}
return &(s->blocks[tblock_number(id)]);
}