52b8375eab41ecad479a5e097ee058077ac54874
[idzebra-moved-to-github.git] / bfile / bfile.c
1 /* This file is part of the Zebra server.
2    Copyright (C) 1994-2009 Index Data
3
4 Zebra is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <assert.h>
24 #ifdef WIN32
25 #include <io.h>
26 #endif
27
28 #if HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31
32 #include <yaz/xmalloc.h>
33 #include <idzebra/util.h>
34 #include <idzebra/bfile.h>
35 #include "mfile.h"
36 #include "cfile.h"
37
38 struct BFile_struct
39 {
40     MFile mf;
41     Zebra_lock_rdwr rdwr_lock;
42     struct CFile_struct *cf;
43     char *alloc_buf;
44     int block_size;
45     int alloc_buf_size;
46     zint last_block;
47     zint free_list;
48     zint root_block;
49     char *magic;
50     int header_dirty;
51 };
52
53 struct BFiles_struct {
54     MFile_area commit_area;
55     MFile_area register_area;
56     char *base;
57     char *cache_fname;
58 };
59
60 BFiles bfs_create (const char *spec, const char *base)
61 {
62     BFiles bfs = (BFiles) xmalloc(sizeof(*bfs));
63     bfs->commit_area = 0;
64     bfs->base = 0;
65     bfs->cache_fname = 0;
66     if (base)
67         bfs->base = xstrdup(base);
68     bfs->register_area = mf_init("register", spec, base, 0);
69     if (!bfs->register_area)
70     {
71         bfs_destroy(bfs);
72         return 0;
73     }
74     return bfs;
75 }
76
77 void bfs_destroy(BFiles bfs)
78 {
79     if (!bfs)
80         return;
81     xfree(bfs->cache_fname);
82     xfree(bfs->base);
83     mf_destroy(bfs->commit_area);
84     mf_destroy(bfs->register_area);
85     xfree(bfs);
86 }
87
88 static FILE *open_cache(BFiles bfs, const char *flags)
89 {
90     FILE *file;
91
92     file = fopen(bfs->cache_fname, flags);
93     return file;
94 }
95
96 static void unlink_cache(BFiles bfs)
97 {
98     if (bfs->cache_fname)
99         unlink(bfs->cache_fname);
100 }
101
102 ZEBRA_RES bf_cache(BFiles bfs, const char *spec)
103 {
104     if (spec)
105     {
106         yaz_log(YLOG_LOG, "enabling shadow spec=%s", spec);
107         if (!bfs->commit_area)
108             bfs->commit_area = mf_init("shadow", spec, bfs->base, 1);
109         if (bfs->commit_area)
110         {
111             bfs->cache_fname = xmalloc(strlen(bfs->commit_area->dirs->name)+
112                                        8);
113             strcpy(bfs->cache_fname, bfs->commit_area->dirs->name);
114             strcat(bfs->cache_fname, "/cache");
115             yaz_log(YLOG_LOG, "cache_fname = %s", bfs->cache_fname);
116         }
117         else
118         {
119             yaz_log(YLOG_WARN, "shadow could not be enabled");
120             return ZEBRA_FAIL;
121         }
122     }
123     else
124         bfs->commit_area = 0;
125     return ZEBRA_OK;
126 }
127
128 int bf_close2(BFile bf)
129 {
130     int ret = 0;
131     zebra_lock_rdwr_destroy(&bf->rdwr_lock);
132     if (bf->cf)
133     {
134         if (cf_close(bf->cf))
135             ret = -1;
136     }
137     if (bf->mf)
138     {
139         if (mf_close(bf->mf))
140             ret = -1;
141     }
142     xfree(bf->alloc_buf);
143     xfree(bf->magic);
144     xfree(bf);
145     return ret;
146 }
147
148 void bf_close(BFile bf)
149 {
150     if (bf_close2(bf))
151     {
152         zebra_exit("bf_close");
153     }
154 }
155
156
157 #define HEADER_SIZE 256
158
159 BFile bf_xopen(BFiles bfs, const char *name, int block_size, int wrflag,
160                const char *magic, int *read_version,
161                const char **more_info)
162 {
163     char read_magic[40];
164     int l = 0;
165     int i = 0;
166     char *hbuf;
167     zint pos = 0;
168     BFile bf = bf_open(bfs, name, block_size, wrflag);
169
170     if (!bf)
171         return 0;
172      /* HEADER_SIZE is considered enough for our header */
173     if (bf->block_size < HEADER_SIZE)
174         bf->alloc_buf_size = HEADER_SIZE;
175     else
176         bf->alloc_buf_size = bf->block_size;
177         
178     hbuf = bf->alloc_buf = xmalloc(bf->alloc_buf_size);
179
180     /* fill-in default values */
181     bf->free_list = 0;
182     bf->root_block = bf->last_block = HEADER_SIZE / bf->block_size + 1;
183     bf->magic = xstrdup(magic);
184
185     if (!bf_read(bf, pos, 0, 0, hbuf + pos * bf->block_size))
186     {
187         if (wrflag)
188             bf->header_dirty = 1;
189         return bf;
190     }
191     while(hbuf[pos * bf->block_size + i] != '\0')
192     {
193         if (i == bf->block_size)
194         {   /* end of block at pos reached .. */
195             if (bf->alloc_buf_size  < (pos+1) * bf->block_size)
196             {
197                 /* not room for all in alloc_buf_size */
198                 yaz_log(YLOG_WARN, "bad header for %s (3)", magic);
199                 bf_close(bf);
200                 return 0;
201             }
202             /* read next block in header */
203             pos++;
204             if (!bf_read(bf, pos, 0, 0, hbuf + pos * bf->block_size))
205             {
206                 yaz_log(YLOG_WARN, "missing header block %s (4)", magic);
207                 bf_close(bf);
208                 return 0;
209             }
210             i = 0; /* pos within block is reset */
211         }
212         else
213             i++;
214     }
215     if (sscanf(hbuf, "%39s %d " ZINT_FORMAT " " ZINT_FORMAT "%n",
216                read_magic, read_version, &bf->last_block,
217                &bf->free_list, &l) < 4 && l)  /* if %n is counted, it's 5 */
218     {
219         yaz_log(YLOG_WARN, "bad header for %s (1)", magic);
220         bf_close(bf);
221         return 0;
222     }
223     if (strcmp(read_magic, magic))
224     {
225         yaz_log(YLOG_WARN, "bad header for %s (2)", magic);
226         bf_close(bf);
227         return 0;
228     }
229     if (hbuf[l] == ' ')
230         l++;
231     if (more_info)
232         *more_info = hbuf + l;
233     return bf;
234 }
235
236 int bf_xclose(BFile bf, int version, const char *more_info)
237 {
238     if (bf->header_dirty)
239     {
240         zint pos = 0;
241         assert(bf->alloc_buf);
242         assert(bf->magic);
243         sprintf(bf->alloc_buf, "%s %d " ZINT_FORMAT " " ZINT_FORMAT " ", 
244                 bf->magic, version, bf->last_block, bf->free_list);
245         if (more_info)
246             strcat(bf->alloc_buf, more_info);
247         while (1)
248         {
249             bf_write(bf, pos, 0, 0, bf->alloc_buf + pos * bf->block_size);
250             pos++;
251             if (pos * bf->block_size > strlen(bf->alloc_buf))
252                 break;
253         }
254     }
255     return bf_close2(bf);
256 }
257
258 BFile bf_open(BFiles bfs, const char *name, int block_size, int wflag)
259 {
260     BFile bf = (BFile) xmalloc(sizeof(*bf));
261
262     bf->alloc_buf = 0;
263     bf->magic = 0;
264     bf->block_size = block_size;
265     bf->header_dirty = 0;
266     bf->cf = 0;
267     bf->mf = 0;
268     zebra_lock_rdwr_init(&bf->rdwr_lock);
269
270     if (bfs->commit_area)
271     {
272         int first_time;
273
274         bf->mf = mf_open(bfs->register_area, name, block_size, 0);
275         bf->cf = cf_open(bf->mf, bfs->commit_area, name, block_size,
276                          wflag, &first_time);
277         if (!bf->cf)
278         {
279             yaz_log(YLOG_FATAL, "cf_open failed for %s", name);
280             bf_close(bf);
281             return 0;
282         }
283         if (first_time)
284         {
285             FILE *outf;
286
287             outf = open_cache(bfs, "ab");
288             if (!outf)
289             {
290                 yaz_log(YLOG_FATAL|YLOG_ERRNO, "open %s", bfs->cache_fname);
291                 bf_close(bf);
292                 return 0;
293             }
294             fprintf(outf, "%s %d\n", name, block_size);
295             if (fclose(outf))
296             {
297                 yaz_log(YLOG_FATAL|YLOG_ERRNO, "fclose %s", bfs->cache_fname);
298                 bf_close(bf);
299                 return 0;
300             }
301         }
302     }
303     else
304     {
305         bf->mf = mf_open(bfs->register_area, name, block_size, wflag);
306     }
307     if (!bf->mf)
308     {
309         yaz_log(YLOG_FATAL, "mf_open failed for %s", name);
310         bf_close(bf);
311         return 0;
312     }
313     return bf;
314 }
315
316 int bf_read(BFile bf, zint no, int offset, int nbytes, void *buf)
317 {
318     int ret = bf_read2(bf, no, offset, nbytes, buf);
319
320     if (ret == -1)
321     {
322         zebra_exit("bf_read");
323     }
324     return ret;
325 }
326
327 int bf_read2(BFile bf, zint no, int offset, int nbytes, void *buf)
328 {
329     int ret;
330
331     zebra_lock_rdwr_rlock(&bf->rdwr_lock);
332     if (bf->cf)
333     {
334         if ((ret = cf_read(bf->cf, no, offset, nbytes, buf)) == 0)
335             ret = mf_read(bf->mf, no, offset, nbytes, buf);
336     }
337     else 
338         ret = mf_read(bf->mf, no, offset, nbytes, buf);
339     zebra_lock_rdwr_runlock(&bf->rdwr_lock);
340     return ret;
341 }
342
343 int bf_write(BFile bf, zint no, int offset, int nbytes, const void *buf)
344 {
345     int ret = bf_write2(bf, no, offset, nbytes, buf);
346
347     if (ret == -1)
348     {
349         zebra_exit("bf_write");
350     }
351     return ret;
352 }
353
354 int bf_write2(BFile bf, zint no, int offset, int nbytes, const void *buf)
355 {
356     int r;
357     zebra_lock_rdwr_wlock(&bf->rdwr_lock);
358     if (bf->cf)
359         r = cf_write(bf->cf, no, offset, nbytes, buf);
360     else
361         r = mf_write(bf->mf, no, offset, nbytes, buf);
362     zebra_lock_rdwr_wunlock(&bf->rdwr_lock);
363     return r;
364 }
365
366 int bf_commitExists(BFiles bfs)
367 {
368     FILE *inf;
369
370     inf = open_cache(bfs, "rb");
371     if (inf)
372     {
373         fclose(inf);
374         return 1;
375     }
376     return 0;
377 }
378
379 void bf_reset(BFiles bfs)
380 {
381     if (!bfs)
382         return;
383     mf_reset(bfs->commit_area, 1);
384     mf_reset(bfs->register_area, 1);
385     unlink_cache(bfs);
386 }
387
388 int bf_commitExec(BFiles bfs)
389 {
390     FILE *inf;
391     int block_size;
392     char path[256];
393     MFile mf;
394     CFile cf;
395     int first_time;
396     int r = 0;
397
398     assert(bfs->commit_area);
399     if (!(inf = open_cache(bfs, "rb")))
400     {
401         yaz_log(YLOG_LOG, "No commit file");
402         return -1;
403     }
404     while (fscanf(inf, "%s %d", path, &block_size) == 2)
405     {
406         mf = mf_open(bfs->register_area, path, block_size, 1);
407         if (!mf)
408         {
409             r = -1;
410             break;
411         }
412         cf = cf_open(mf, bfs->commit_area, path, block_size, 0, &first_time);
413         if (!cf)
414         {
415             mf_close(mf);
416             r = -1;
417             break;
418         }
419
420         r = cf_commit(cf);
421
422         cf_close(cf);
423         mf_close(mf);
424
425         if (r)
426             break;
427     }
428     fclose(inf);
429     return r;
430 }
431
432 void bf_commitClean(BFiles bfs, const char *spec)
433 {
434     int mustDisable = 0;
435
436     if (!bfs->commit_area)
437     {
438         bf_cache(bfs, spec);
439         mustDisable = 1;
440     }
441
442     mf_reset(bfs->commit_area, 1);
443
444     unlink_cache(bfs);
445     if (mustDisable)
446         bf_cache(bfs, 0);
447 }
448
449 int bf_alloc(BFile bf, int no, zint *blocks)
450 {
451     int i;
452     assert(bf->alloc_buf);
453     bf->header_dirty = 1;
454     for (i = 0; i < no; i++)
455     {
456         if (!bf->free_list)
457             blocks[i] = bf->last_block++;
458         else
459         {
460             char buf[16];
461             const char *cp = buf;
462             memset(buf, '\0', sizeof(buf));
463
464             blocks[i] = bf->free_list;
465             if (bf_read(bf, bf->free_list, 0, sizeof(buf), buf) != 1)
466             {
467                 yaz_log(YLOG_WARN, "Bad freelist entry " ZINT_FORMAT,
468                         bf->free_list);
469                 return -1;
470             }
471             zebra_zint_decode(&cp, &bf->free_list);
472         }
473     }
474     return 0;
475 }
476
477 int bf_free(BFile bf, int no, const zint *blocks)
478 {
479     int i;
480     assert(bf->alloc_buf);
481     bf->header_dirty = 1;
482     for (i = 0; i < no; i++)
483     {
484         char buf[16];
485         char *cp = buf;
486         memset(buf, '\0', sizeof(buf));
487         zebra_zint_encode(&cp, bf->free_list);
488         bf->free_list = blocks[i];
489         bf_write(bf, bf->free_list, 0, sizeof(buf), buf);
490     }
491     return 0;
492 }
493
494 int bfs_register_directory_stat(BFiles bfs, int no, const char **directory,
495                                 double *used_bytes, double *max_bytes)
496 {
497     return mf_area_directory_stat(bfs->register_area, no, directory,
498                                   used_bytes, max_bytes);
499 }
500
501
502 int bfs_shadow_directory_stat(BFiles bfs, int no, const char **directory,
503                               double *used_bytes, double *max_bytes)
504 {
505     if (!bfs->commit_area)
506         return 0;
507     return mf_area_directory_stat(bfs->commit_area, no, directory,
508                                   used_bytes, max_bytes);
509 }
510 /*
511  * Local variables:
512  * c-basic-offset: 4
513  * indent-tabs-mode: nil
514  * End:
515  * vim: shiftwidth=4 tabstop=8 expandtab
516  */
517