ab5fa6cb26d960b4038e055b34287f478e0c51b7
[idzebra-moved-to-github.git] / bfile / bfile.c
1 /* $Id: bfile.c,v 1.43 2005-05-17 08:50:48 adam Exp $
2    Copyright (C) 1995-2005
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27 #ifdef WIN32
28 #include <io.h>
29 #else
30 #include <unistd.h>
31 #endif
32
33 #include <yaz/xmalloc.h>
34 #include <idzebra/util.h>
35 #include <idzebra/bfile.h>
36 #include "mfile.h"
37 #include "cfile.h"
38
39 struct BFile_struct
40 {
41     MFile mf;
42     Zebra_lock_rdwr rdwr_lock;
43     struct CFile_struct *cf;
44     char *alloc_buf;
45     int block_size;
46     int alloc_buf_size;
47     zint last_block;
48     zint free_list;
49     zint root_block;
50     char *magic;
51     int header_dirty;
52 };
53
54 struct BFiles_struct {
55     MFile_area commit_area;
56     MFile_area_struct *register_area;
57     char *base;
58     char *cache_fname;
59 };
60
61 BFiles bfs_create (const char *spec, const char *base)
62 {
63     BFiles bfs = (BFiles) xmalloc (sizeof(*bfs));
64     bfs->commit_area = NULL;
65     bfs->base = 0;
66     bfs->cache_fname = 0;
67     if (base)
68         bfs->base = xstrdup (base);
69     bfs->register_area = mf_init("register", spec, base);
70     if (!bfs->register_area)
71     {
72         bfs_destroy(bfs);
73         return 0;
74     }
75     return bfs;
76 }
77
78 void bfs_destroy (BFiles bfs)
79 {
80     if (!bfs)
81         return;
82     xfree (bfs->cache_fname);
83     xfree (bfs->base);
84     mf_destroy (bfs->commit_area);
85     mf_destroy (bfs->register_area);
86     xfree (bfs);
87 }
88
89 static FILE *open_cache (BFiles bfs, const char *flags)
90 {
91     FILE *file;
92
93     file = fopen (bfs->cache_fname, flags);
94     return file;
95 }
96
97 static void unlink_cache (BFiles bfs)
98 {
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);
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 = NULL;
125     return ZEBRA_OK;
126 }
127
128 int bf_close (BFile bf)
129 {
130     zebra_lock_rdwr_destroy (&bf->rdwr_lock);
131     if (bf->cf)
132         cf_close (bf->cf);
133     mf_close (bf->mf);
134     xfree(bf->alloc_buf);
135     xfree(bf->magic);
136     xfree(bf);
137     return 0;
138 }
139
140 #define HEADER_SIZE 256
141
142 BFile bf_xopen(BFiles bfs, const char *name, int block_size, int wrflag,
143                const char *magic, int *read_version,
144                const char **more_info)
145 {
146     char read_magic[40];
147     int l = 0;
148     int i = 0;
149     char *hbuf;
150     zint pos = 0;
151     BFile bf = bf_open(bfs, name, block_size, wrflag);
152
153     if (!bf)
154         return 0;
155      /* HEADER_SIZE is considered enough for our header */
156     if (bf->alloc_buf_size < HEADER_SIZE)
157         bf->alloc_buf_size = HEADER_SIZE;
158     else
159         bf->alloc_buf_size = bf->block_size;
160         
161     hbuf = bf->alloc_buf = xmalloc(bf->alloc_buf_size);
162
163     /* fill-in default values */
164     bf->free_list = 0;
165     bf->root_block = bf->last_block = HEADER_SIZE / bf->block_size + 1;
166     bf->magic = xstrdup(magic);
167
168     if (!bf_read(bf, pos, 0, 0, hbuf + pos * bf->block_size))
169     {
170         if (wrflag)
171             bf->header_dirty = 1;
172         return bf;
173     }
174     while(hbuf[pos * bf->block_size + i] != '\0')
175     {
176         if (i == bf->block_size)
177         {   /* end of block at pos reached .. */
178             if (bf->alloc_buf_size  < (pos+1) * bf->block_size)
179             {
180                 /* not room for all in alloc_buf_size */
181                 yaz_log(YLOG_WARN, "bad header for %s (3)", magic);
182                 bf_close(bf);
183                 return 0;
184             }
185             /* read next block in header */
186             pos++;
187             if (!bf_read(bf, pos, 0, 0, hbuf + pos * bf->block_size))
188             {
189                 yaz_log(YLOG_WARN, "missing header block %s (4)", magic);
190                 bf_close(bf);
191                 return 0;
192             }
193             i = 0; /* pos within block is reset */
194         }
195         else
196             i++;
197     }
198     if (sscanf(hbuf, "%39s %d " ZINT_FORMAT " " ZINT_FORMAT "%n",
199                read_magic, read_version, &bf->last_block,
200                &bf->free_list, &l) < 4 && l)  /* if %n is counted, it's 5 */
201     {
202         yaz_log(YLOG_WARN, "bad header for %s (1)", magic);
203         bf_close(bf);
204         return 0;
205     }
206     if (strcmp(read_magic, magic))
207     {
208         yaz_log(YLOG_WARN, "bad header for %s (2)", magic);
209         bf_close(bf);
210         return 0;
211     }
212     if (more_info)
213         *more_info = hbuf + l;
214     return bf;
215 }
216
217 int bf_xclose (BFile bf, int version, const char *more_info)
218 {
219     if (bf->header_dirty)
220     {
221         zint pos = 0;
222         assert(bf->alloc_buf);
223         assert(bf->magic);
224         sprintf(bf->alloc_buf, "%s %d " ZINT_FORMAT " " ZINT_FORMAT " ", 
225                 bf->magic, version, bf->last_block, bf->free_list);
226         if (more_info)
227             strcat(bf->alloc_buf, more_info);
228         while (1)
229         {
230             bf_write(bf, pos, 0, 0, bf->alloc_buf + pos * bf->block_size);
231             pos++;
232             if (pos * bf->block_size > strlen(bf->alloc_buf))
233                 break;
234         }
235     }
236     return bf_close(bf);
237 }
238
239 BFile bf_open (BFiles bfs, const char *name, int block_size, int wflag)
240 {
241     BFile bf = (BFile) xmalloc(sizeof(struct BFile_struct));
242
243     bf->alloc_buf = 0;
244     bf->magic = 0;
245     bf->block_size = block_size;
246     bf->header_dirty = 0;
247     if (bfs->commit_area)
248     {
249         int first_time;
250
251         bf->mf = mf_open (bfs->register_area, name, block_size, 0);
252         bf->cf = cf_open (bf->mf, bfs->commit_area, name, block_size,
253                            wflag, &first_time);
254         if (first_time)
255         {
256             FILE *outf;
257
258             outf = open_cache(bfs, "ab");
259             if (!outf)
260             {
261                 yaz_log(YLOG_FATAL|YLOG_ERRNO, "open %s", bfs->cache_fname);
262                 exit(1);
263             }
264             fprintf(outf, "%s %d\n", name, block_size);
265             fclose(outf);
266         }
267     }
268     else
269     {
270         bf->mf = mf_open(bfs->register_area, name, block_size, wflag);
271         bf->cf = NULL;
272     }
273     if (!bf->mf)
274     {
275         yaz_log(YLOG_FATAL, "mf_open failed for %s", name);
276         xfree(bf);
277         return 0;
278     }
279     zebra_lock_rdwr_init(&bf->rdwr_lock);
280     return bf;
281 }
282
283 int bf_read (BFile bf, zint no, int offset, int nbytes, void *buf)
284 {
285     int r;
286
287     zebra_lock_rdwr_rlock (&bf->rdwr_lock);
288     if (bf->cf)
289     {
290         if ((r = cf_read (bf->cf, no, offset, nbytes, buf)) == -1)
291             r = mf_read (bf->mf, no, offset, nbytes, buf);
292     }
293     else 
294         r = mf_read (bf->mf, no, offset, nbytes, buf);
295     zebra_lock_rdwr_runlock (&bf->rdwr_lock);
296     return r;
297 }
298
299 int bf_write (BFile bf, zint no, int offset, int nbytes, const void *buf)
300 {
301     int r;
302     zebra_lock_rdwr_wlock (&bf->rdwr_lock);
303     if (bf->cf)
304         r = cf_write (bf->cf, no, offset, nbytes, buf);
305     else
306         r = mf_write (bf->mf, no, offset, nbytes, buf);
307     zebra_lock_rdwr_wunlock (&bf->rdwr_lock);
308     return r;
309 }
310
311 int bf_commitExists (BFiles bfs)
312 {
313     FILE *inf;
314
315     inf = open_cache (bfs, "rb");
316     if (inf)
317     {
318         fclose (inf);
319         return 1;
320     }
321     return 0;
322 }
323
324 void bf_reset (BFiles bfs)
325 {
326     if (!bfs)
327         return;
328     mf_reset (bfs->commit_area);
329     mf_reset (bfs->register_area);
330 }
331
332 void bf_commitExec (BFiles bfs)
333 {
334     FILE *inf;
335     int block_size;
336     char path[256];
337     MFile mf;
338     CFile cf;
339     int first_time;
340
341     assert (bfs->commit_area);
342     if (!(inf = open_cache (bfs, "rb")))
343     {
344         yaz_log (YLOG_LOG, "No commit file");
345         return ;
346     }
347     while (fscanf (inf, "%s %d", path, &block_size) == 2)
348     {
349         mf = mf_open (bfs->register_area, path, block_size, 1);
350         cf = cf_open (mf, bfs->commit_area, path, block_size, 0, &first_time);
351
352         cf_commit (cf);
353
354         cf_close (cf);
355         mf_close (mf);
356     }
357     fclose (inf);
358 }
359
360 void bf_commitClean (BFiles bfs, const char *spec)
361 {
362     FILE *inf;
363     int block_size;
364     char path[256];
365     MFile mf;
366     CFile cf;
367     int mustDisable = 0;
368     int firstTime;
369
370     if (!bfs->commit_area)
371     {
372         bf_cache (bfs, spec);
373         mustDisable = 1;
374     }
375
376     if (!(inf = open_cache (bfs, "rb")))
377         return ;
378     while (fscanf (inf, "%s %d", path, &block_size) == 2)
379     {
380         mf = mf_open (bfs->register_area, path, block_size, 0);
381         cf = cf_open (mf, bfs->commit_area, path, block_size, 1, &firstTime);
382         cf_unlink (cf);
383         cf_close (cf);
384         mf_close (mf);
385     }
386     fclose (inf);
387     unlink_cache (bfs);
388     if (mustDisable)
389         bf_cache (bfs, 0);
390 }
391
392 int bf_alloc(BFile bf, int no, zint *blocks)
393 {
394     int i;
395     assert(bf->alloc_buf);
396     bf->header_dirty = 1;
397     for (i = 0; i < no; i++)
398     {
399         if (!bf->free_list)
400             blocks[i] = bf->last_block++;
401         else
402         {
403             char buf[16];
404             const char *cp = buf;
405             memset(buf, '\0', sizeof(buf));
406
407             blocks[i] = bf->free_list;
408             if (!bf_read(bf, bf->free_list, 0, sizeof(buf), buf))
409             {
410                 yaz_log(YLOG_WARN, "Bad freelist entry " ZINT_FORMAT,
411                         bf->free_list);
412                 exit(1);
413             }
414             zebra_zint_decode(&cp, &bf->free_list);
415         }
416     }
417     return 0;
418 }
419
420 int bf_free(BFile bf, int no, const zint *blocks)
421 {
422     int i;
423     assert(bf->alloc_buf);
424     bf->header_dirty = 1;
425     for (i = 0; i < no; i++)
426     {
427         char buf[16];
428         char *cp = buf;
429         memset(buf, '\0', sizeof(buf));
430         zebra_zint_encode(&cp, bf->free_list);
431         bf->free_list = blocks[i];
432         bf_write(bf, bf->free_list, 0, sizeof(buf), buf);
433     }
434     return 0;
435 }