d5cf5351 |
1 | #define _LARGEFILE64_SOURCE |
2 | #define _XOPEN_SOURCE 500 |
3 | #include <stdlib.h> |
4 | #include <unistd.h> |
5 | #include <stdio.h> |
6 | #include <fcntl.h> |
7 | #include <errno.h> |
8 | #include <string.h> |
9 | #include <sys/types.h> |
10 | #include <sys/stat.h> |
11 | #include <gcrypt.h> |
12 | #include <assert.h> |
13 | |
14 | #include "utils.h" |
15 | #include "store.h" |
16 | #include "log.h" |
17 | |
18 | #define LOGMAGIC "Dolda/Venti-1" |
19 | #define IDXMAGIC "Dolda/Index-1" |
20 | #define LOGENTMAGIC "\xca\xe5\x7a\x93" |
21 | |
22 | typedef loff_t idx_t; |
23 | |
24 | struct loghdr { |
25 | char magic[sizeof(LOGMAGIC)]; |
26 | }; |
27 | |
28 | struct idxhdr { |
29 | char magic[sizeof(IDXMAGIC)]; |
30 | u_int64_t size; |
31 | }; |
32 | |
33 | struct idxent { |
34 | struct addr addr; |
35 | u_int64_t l, r; |
36 | u_int64_t off; |
37 | }; |
38 | |
39 | struct logent { |
40 | u_int8_t magic[4]; |
41 | struct addr name; |
42 | u_int16_t len; |
43 | u_int8_t fl; |
44 | }; |
45 | |
46 | struct fstore { |
47 | int logfd; |
48 | int idxfd; |
49 | loff_t logsize; |
50 | idx_t idxsize; |
51 | }; |
52 | |
53 | static int release(struct fstore *fst) |
54 | { |
55 | if(fst->logfd >= 0) { |
56 | fsync(fst->logfd); |
57 | close(fst->logfd); |
58 | } |
59 | if(fst->idxfd >= 0) { |
60 | fsync(fst->idxfd); |
61 | close(fst->idxfd); |
62 | } |
63 | free(fst); |
64 | return(0); |
65 | } |
66 | |
67 | static int releaseg(struct store *st) |
68 | { |
69 | return(release(st->pdata)); |
70 | } |
71 | |
72 | static void hash(const void *buf, size_t len, struct addr *a) |
73 | { |
74 | gcry_md_hash_buffer(GCRY_MD_SHA256, a->hash, buf, len); |
75 | } |
76 | |
77 | static int getidx(struct fstore *fst, idx_t i, struct idxent *ie) |
78 | { |
79 | return(readall(fst->idxfd, ie, sizeof(*ie), sizeof(struct idxhdr) + i * sizeof(struct idxent))); |
80 | } |
81 | |
82 | static int putidx(struct fstore *fst, idx_t i, struct idxent *ie) |
83 | { |
84 | return(writeall(fst->idxfd, ie, sizeof(*ie), sizeof(struct idxhdr) + i * sizeof(struct idxent))); |
85 | } |
86 | |
87 | static idx_t lookup(struct fstore *fst, struct addr *a, idx_t *parent) |
88 | { |
89 | idx_t i; |
90 | struct idxent ie; |
91 | int c; |
92 | |
93 | if(fst->idxsize == 0) { |
94 | if(parent != NULL) |
95 | *parent = -1; |
96 | return(-1); |
97 | } |
98 | i = 0; |
99 | while(1) { |
100 | assert(!getidx(fst, i, &ie)); |
101 | c = addrcmp(a, &ie.addr); |
102 | if(c < 0) { |
103 | if(ie.l == 0) { |
104 | if(parent != NULL) |
105 | *parent = i; |
106 | return(-1); |
107 | } |
108 | i = ie.l; |
109 | } else if(c > 0) { |
110 | if(ie.r == 0) { |
111 | if(parent != NULL) |
112 | *parent = i; |
113 | return(-1); |
114 | } |
115 | i = ie.r; |
116 | } else { |
117 | return(i); |
118 | } |
119 | } |
120 | } |
121 | |
122 | static idx_t newindex(struct fstore *fst) |
123 | { |
124 | size_t newsize; |
125 | idx_t ni; |
126 | struct idxent ne; |
127 | struct idxhdr ih; |
128 | |
129 | /* XXX: Thread safety */ |
130 | ni = fst->idxsize++; |
131 | newsize = sizeof(struct idxhdr) + fst->idxsize * sizeof(struct idxent); |
132 | if(ftruncate(fst->idxfd, newsize)) |
133 | return(-1); |
134 | ne.l = ne.r = 0; |
135 | assert(!putidx(fst, ni, &ne)); |
136 | assert(!readall(fst->idxfd, &ih, sizeof(ih), 0)); |
137 | ih.size = fst->idxsize; |
138 | assert(!writeall(fst->idxfd, &ih, sizeof(ih), 0)); |
139 | return(ni); |
140 | } |
141 | |
142 | static int put(struct store *st, const void *buf, size_t len, struct addr *at) |
143 | { |
144 | struct fstore *fst; |
145 | struct addr pa; |
146 | idx_t i, pi; |
147 | struct idxent ie; |
148 | loff_t leoff; |
149 | int c; |
150 | struct logent le; |
151 | |
152 | if(len > STORE_MAXBLSZ) { |
153 | errno = E2BIG; |
154 | return(-1); |
155 | } |
156 | |
157 | fst = st->pdata; |
158 | hash(buf, len, &pa); |
159 | if(at != NULL) |
160 | memcpy(at->hash, pa.hash, 32); |
161 | |
162 | if(lookup(fst, &pa, &pi) != -1) |
163 | return(0); |
164 | |
165 | memcpy(le.magic, LOGENTMAGIC, 4); |
166 | le.name = pa; |
167 | le.len = len; |
168 | le.fl = 0; |
169 | /* XXX: Thread safety { */ |
170 | leoff = fst->logsize; |
171 | fst->logsize += sizeof(le) + len; |
172 | /* } */ |
173 | /* XXX: Handle data with embedded LOGENTMAGIC */ |
174 | writeall(fst->logfd, &le, sizeof(le), leoff); |
175 | writeall(fst->logfd, buf, len, leoff + sizeof(le)); |
176 | |
177 | i = newindex(fst); |
178 | assert(!getidx(fst, i, &ie)); |
179 | ie.addr = pa; |
180 | ie.off = leoff; |
181 | assert(!putidx(fst, i, &ie)); |
182 | if(pi != -1) { |
183 | assert(!getidx(fst, pi, &ie)); |
184 | c = addrcmp(&pa, &ie.addr); |
185 | if(c < 0) |
186 | ie.l = i; |
187 | else |
188 | ie.r = i; |
189 | assert(!putidx(fst, pi, &ie)); |
190 | } |
191 | |
192 | return(0); |
193 | } |
194 | |
195 | #define min(a, b) (((b) < (a))?(b):(a)) |
196 | |
197 | static ssize_t get(struct store *st, void *buf, size_t len, struct addr *at) |
198 | { |
199 | idx_t i; |
200 | struct idxent ie; |
201 | struct fstore *fst; |
202 | struct logent le; |
203 | struct addr v; |
204 | char tmpbuf[STORE_MAXBLSZ]; |
205 | |
206 | fst = st->pdata; |
207 | if((i = lookup(fst, at, NULL)) == -1) { |
208 | errno = ENOENT; |
209 | return(-1); |
210 | } |
211 | assert(!getidx(fst, i, &ie)); |
212 | |
213 | if(readall(fst->logfd, &le, sizeof(le), ie.off)) { |
214 | flog(LOG_CRIT, "could not read log entry: %s", strerror(errno)); |
215 | errno = EIO; |
216 | return(-1); |
217 | } |
218 | if(memcmp(le.magic, LOGENTMAGIC, 4)) { |
219 | flog(LOG_CRIT, "invalid magic in log"); |
220 | errno = EIO; |
221 | return(-1); |
222 | } |
223 | if(addrcmp(&le.name, at)) { |
224 | flog(LOG_CRIT, "did not receive correct block from log"); |
225 | errno = EIO; |
226 | return(-1); |
227 | } |
228 | if(readall(fst->logfd, tmpbuf, le.len, ie.off + sizeof(le))) { |
229 | flog(LOG_CRIT, "could not read log data: %s", strerror(errno)); |
230 | errno = EIO; |
231 | return(-1); |
232 | } |
233 | hash(tmpbuf, le.len, &v); |
234 | if(addrcmp(&v, &le.name)) { |
235 | flog(LOG_CRIT, "log data did not verify against hash"); |
236 | errno = EIO; |
237 | return(-1); |
238 | } |
239 | if(buf != NULL) |
240 | memcpy(buf, tmpbuf, min(len, le.len)); |
241 | return(le.len); |
242 | } |
243 | |
244 | static struct storeops fstops = { |
245 | .release = releaseg, |
246 | .put = put, |
247 | .get = get, |
248 | }; |
249 | |
250 | struct store *newfstore(char *dir) |
251 | { |
252 | struct store *st; |
253 | struct fstore *fst; |
254 | char tbuf[1024]; |
255 | struct loghdr lh; |
256 | struct idxhdr ih; |
257 | struct stat64 sb; |
258 | |
259 | fst = calloc(1, sizeof(*fst)); |
260 | fst->logfd = -1; |
261 | fst->idxfd = -1; |
262 | |
263 | snprintf(tbuf, sizeof(tbuf), "%s/log", dir); |
264 | if((fst->logfd = open(tbuf, O_RDWR | O_LARGEFILE)) < 0) { |
265 | flog(LOG_ERR, "could not open log %s: %s", tbuf, strerror(errno)); |
266 | release(fst); |
267 | return(NULL); |
268 | } |
269 | if(fstat64(fst->logfd, &sb)) { |
270 | flog(LOG_ERR, "could not stat log: %s", strerror(errno)); |
271 | release(fst); |
272 | return(NULL); |
273 | } |
274 | fst->logsize = sb.st_size; |
275 | if(readall(fst->logfd, &lh, sizeof(lh), 0)) { |
276 | flog(LOG_ERR, "could not read log header: %s", strerror(errno)); |
277 | release(fst); |
278 | return(NULL); |
279 | } |
280 | if(memcmp(lh.magic, LOGMAGIC, sizeof(LOGMAGIC))) { |
281 | flog(LOG_ERR, "invalid log magic"); |
282 | release(fst); |
283 | return(NULL); |
284 | } |
285 | |
286 | snprintf(tbuf, sizeof(tbuf), "%s/index", dir); |
287 | if((fst->idxfd = open(tbuf, O_RDWR | O_LARGEFILE)) < 0) { |
288 | flog(LOG_ERR, "could not open index %s: %s", tbuf, strerror(errno)); |
289 | release(fst); |
290 | return(NULL); |
291 | } |
292 | if(fstat64(fst->idxfd, &sb)) { |
293 | flog(LOG_ERR, "could not stat index: %s", strerror(errno)); |
294 | release(fst); |
295 | return(NULL); |
296 | } |
297 | if(readall(fst->idxfd, &ih, sizeof(ih), 0)) { |
298 | flog(LOG_ERR, "could not read index header: %s", strerror(errno)); |
299 | release(fst); |
300 | return(NULL); |
301 | } |
302 | if(memcmp(ih.magic, IDXMAGIC, sizeof(IDXMAGIC))) { |
303 | flog(LOG_ERR, "invalid index magic"); |
304 | release(fst); |
305 | return(NULL); |
306 | } |
307 | if(sb.st_size != (sizeof(struct idxhdr) + ih.size * sizeof(struct idxent))) { |
308 | flog(LOG_ERR, "invalid index size"); |
309 | release(fst); |
310 | return(NULL); |
311 | } |
312 | fst->idxsize = ih.size; |
313 | |
314 | st = newstore(&fstops); |
315 | st->pdata = fst; |
316 | return(st); |
317 | } |
318 | |
319 | int mkfstore(char *dir) |
320 | { |
321 | char tbuf[1024]; |
322 | int fd; |
323 | struct loghdr lh; |
324 | struct idxhdr ih; |
325 | |
326 | if(access(dir, F_OK)) { |
327 | if(mkdir(dir, 0700)) { |
328 | flog(LOG_ERR, "could not create %s: %s", dir, strerror(errno)); |
329 | return(-1); |
330 | } |
331 | } |
332 | |
333 | snprintf(tbuf, sizeof(tbuf), "%s/log", dir); |
334 | if((fd = open(tbuf, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) { |
335 | flog(LOG_ERR, "could not create log %s: %s", tbuf, strerror(errno)); |
336 | return(-1); |
337 | } |
338 | memcpy(lh.magic, LOGMAGIC, sizeof(LOGMAGIC)); |
339 | if(writeall(fd, &lh, sizeof(lh), 0)) { |
340 | flog(LOG_ERR, "could not write log header: %s", strerror(errno)); |
341 | close(fd); |
342 | return(-1); |
343 | } |
344 | close(fd); |
345 | |
346 | snprintf(tbuf, sizeof(tbuf), "%s/index", dir); |
347 | if((fd = open(tbuf, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) { |
348 | flog(LOG_ERR, "could not create index %s: %s", tbuf, strerror(errno)); |
349 | return(-1); |
350 | } |
351 | memcpy(ih.magic, IDXMAGIC, sizeof(IDXMAGIC)); |
352 | ih.size = 0; |
353 | if(writeall(fd, &ih, sizeof(ih), 0)) { |
354 | flog(LOG_ERR, "could not write index header: %s", strerror(errno)); |
355 | close(fd); |
356 | return(-1); |
357 | } |
358 | close(fd); |
359 | return(0); |
360 | } |