Remove btappend.
[vcfs.git] / filestore.c
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 }