Use or instead of add.
[vcfs.git] / filestore.c
CommitLineData
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
22typedef loff_t idx_t;
23
24struct loghdr {
25 char magic[sizeof(LOGMAGIC)];
26};
27
28struct idxhdr {
29 char magic[sizeof(IDXMAGIC)];
30 u_int64_t size;
31};
32
33struct idxent {
34 struct addr addr;
35 u_int64_t l, r;
36 u_int64_t off;
37};
38
39struct logent {
40 u_int8_t magic[4];
41 struct addr name;
42 u_int16_t len;
43 u_int8_t fl;
44};
45
46struct fstore {
47 int logfd;
48 int idxfd;
49 loff_t logsize;
50 idx_t idxsize;
51};
52
53static 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
67static int releaseg(struct store *st)
68{
69 return(release(st->pdata));
70}
71
72static 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
77static 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
82static 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
87static 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
122static 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
142static 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
197static 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
244static struct storeops fstops = {
245 .release = releaseg,
246 .put = put,
247 .get = get,
248};
249
250struct 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
319int 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}