facec01ef09c48fb7c1a32342f9178e7c2a4c4c4
[idzebra-moved-to-github.git] / bfile / bfile.c
1 /* $Id: bfile.c,v 1.40 2005-03-30 09:25:23 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 <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_struct *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 = NULL;
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);
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     unlink (bfs->cache_fname);
99 }
100
101 void bf_cache (BFiles bfs, const char *spec)
102 {
103     if (spec)
104     {
105         yaz_log (YLOG_LOG, "enabling cache spec=%s", spec);
106         if (!bfs->commit_area)
107             bfs->commit_area = mf_init ("shadow", spec, bfs->base);
108         if (bfs->commit_area)
109         {
110             bfs->cache_fname = xmalloc (strlen(bfs->commit_area->dirs->name)+
111                                        8);
112             strcpy (bfs->cache_fname, bfs->commit_area->dirs->name);
113             strcat (bfs->cache_fname, "/cache");
114             yaz_log (YLOG_LOG, "cache_fname = %s", bfs->cache_fname);
115         }
116     }
117     else
118         bfs->commit_area = NULL;
119 }
120
121 int bf_close (BFile bf)
122 {
123     zebra_lock_rdwr_destroy (&bf->rdwr_lock);
124     if (bf->cf)
125         cf_close (bf->cf);
126     mf_close (bf->mf);
127     xfree(bf->alloc_buf);
128     xfree(bf->magic);
129     xfree(bf);
130     return 0;
131 }
132
133 #define HEADER_SIZE 256
134
135 BFile bf_xopen(BFiles bfs, const char *name, int block_size, int wrflag,
136                const char *magic, int *read_version,
137                const char **more_info)
138 {
139     char read_magic[40];
140     int l = 0;
141     int i = 0;
142     char *hbuf;
143     zint pos = 0;
144     BFile bf = bf_open(bfs, name, block_size, wrflag);
145
146     if (!bf)
147         return 0;
148      /* HEADER_SIZE is considered enough for our header */
149     if (bf->alloc_buf_size < HEADER_SIZE)
150         bf->alloc_buf_size = HEADER_SIZE;
151     else
152         bf->alloc_buf_size = bf->block_size;
153         
154     hbuf = bf->alloc_buf = xmalloc(bf->alloc_buf_size);
155
156     /* fill-in default values */
157     bf->free_list = 0;
158     bf->root_block = bf->last_block = HEADER_SIZE / bf->block_size + 1;
159     bf->magic = xstrdup(magic);
160
161     if (!bf_read(bf, pos, 0, 0, hbuf + pos * bf->block_size))
162     {
163         if (wrflag)
164             bf->header_dirty = 1;
165         return bf;
166     }
167     while(hbuf[pos * bf->block_size + i] != '\0')
168     {
169         if (i == bf->block_size)
170         {   /* end of block at pos reached .. */
171             if (bf->alloc_buf_size  < (pos+1) * bf->block_size)
172             {
173                 /* not room for all in alloc_buf_size */
174                 yaz_log(YLOG_WARN, "bad header for %s (3)", magic);
175                 bf_close(bf);
176                 return 0;
177             }
178             /* read next block in header */
179             pos++;
180             if (!bf_read(bf, pos, 0, 0, hbuf + pos * bf->block_size))
181             {
182                 yaz_log(YLOG_WARN, "missing header block %s (4)", magic);
183                 bf_close(bf);
184                 return 0;
185             }
186             i = 0; /* pos within block is reset */
187         }
188         else
189             i++;
190     }
191     if (sscanf(hbuf, "%39s %d " ZINT_FORMAT " " ZINT_FORMAT "%n",
192                read_magic, read_version, &bf->last_block,
193                &bf->free_list, &l) < 4 && l)  /* if %n is counted, it's 5 */
194     {
195         yaz_log(YLOG_WARN, "bad header for %s (1)", magic);
196         bf_close(bf);
197         return 0;
198     }
199     if (strcmp(read_magic, magic))
200     {
201         yaz_log(YLOG_WARN, "bad header for %s (2)", magic);
202         bf_close(bf);
203         return 0;
204     }
205     if (more_info)
206         *more_info = hbuf + l;
207     return bf;
208 }
209
210 int bf_xclose (BFile bf, int version, const char *more_info)
211 {
212     if (bf->header_dirty)
213     {
214         assert(bf->alloc_buf);
215         zint pos = 0;
216         assert(bf->magic);
217         sprintf(bf->alloc_buf, "%s %d " ZINT_FORMAT " " ZINT_FORMAT " ", 
218                 bf->magic, version, bf->last_block, bf->free_list);
219         if (more_info)
220             strcat(bf->alloc_buf, more_info);
221         while (1)
222         {
223             bf_write(bf, pos, 0, 0, bf->alloc_buf + pos * bf->block_size);
224             pos++;
225             if (pos * bf->block_size > strlen(bf->alloc_buf))
226                 break;
227         }
228     }
229     return bf_close(bf);
230 }
231
232 BFile bf_open (BFiles bfs, const char *name, int block_size, int wflag)
233 {
234     BFile bf = (BFile) xmalloc(sizeof(struct BFile_struct));
235
236     bf->alloc_buf = 0;
237     bf->magic = 0;
238     bf->block_size = block_size;
239     bf->header_dirty = 0;
240     if (bfs->commit_area)
241     {
242         int first_time;
243
244         bf->mf = mf_open (bfs->register_area, name, block_size, 0);
245         bf->cf = cf_open (bf->mf, bfs->commit_area, name, block_size,
246                            wflag, &first_time);
247         if (first_time)
248         {
249             FILE *outf;
250
251             outf = open_cache(bfs, "ab");
252             if (!outf)
253             {
254                 yaz_log(YLOG_FATAL|YLOG_ERRNO, "open %s", bfs->cache_fname);
255                 exit(1);
256             }
257             fprintf(outf, "%s %d\n", name, block_size);
258             fclose(outf);
259         }
260     }
261     else
262     {
263         bf->mf = mf_open(bfs->register_area, name, block_size, wflag);
264         bf->cf = NULL;
265     }
266     if (!bf->mf)
267     {
268         yaz_log(YLOG_FATAL, "mf_open failed for %s", name);
269         xfree(bf);
270         return 0;
271     }
272     zebra_lock_rdwr_init(&bf->rdwr_lock);
273     return bf;
274 }
275
276 int bf_read (BFile bf, zint no, int offset, int nbytes, void *buf)
277 {
278     int r;
279
280     zebra_lock_rdwr_rlock (&bf->rdwr_lock);
281     if (bf->cf)
282     {
283         if ((r = cf_read (bf->cf, no, offset, nbytes, buf)) == -1)
284             r = mf_read (bf->mf, no, offset, nbytes, buf);
285     }
286     else 
287         r = mf_read (bf->mf, no, offset, nbytes, buf);
288     zebra_lock_rdwr_runlock (&bf->rdwr_lock);
289     return r;
290 }
291
292 int bf_write (BFile bf, zint no, int offset, int nbytes, const void *buf)
293 {
294     int r;
295     zebra_lock_rdwr_wlock (&bf->rdwr_lock);
296     if (bf->cf)
297         r = cf_write (bf->cf, no, offset, nbytes, buf);
298     else
299         r = mf_write (bf->mf, no, offset, nbytes, buf);
300     zebra_lock_rdwr_wunlock (&bf->rdwr_lock);
301     return r;
302 }
303
304 int bf_commitExists (BFiles bfs)
305 {
306     FILE *inf;
307
308     inf = open_cache (bfs, "rb");
309     if (inf)
310     {
311         fclose (inf);
312         return 1;
313     }
314     return 0;
315 }
316
317 void bf_reset (BFiles bfs)
318 {
319     if (!bfs)
320         return;
321     mf_reset (bfs->commit_area);
322     mf_reset (bfs->register_area);
323 }
324
325 void bf_commitExec (BFiles bfs)
326 {
327     FILE *inf;
328     int block_size;
329     char path[256];
330     MFile mf;
331     CFile cf;
332     int first_time;
333
334     assert (bfs->commit_area);
335     if (!(inf = open_cache (bfs, "rb")))
336     {
337         yaz_log (YLOG_LOG, "No commit file");
338         return ;
339     }
340     while (fscanf (inf, "%s %d", path, &block_size) == 2)
341     {
342         mf = mf_open (bfs->register_area, path, block_size, 1);
343         cf = cf_open (mf, bfs->commit_area, path, block_size, 0, &first_time);
344
345         cf_commit (cf);
346
347         cf_close (cf);
348         mf_close (mf);
349     }
350     fclose (inf);
351 }
352
353 void bf_commitClean (BFiles bfs, const char *spec)
354 {
355     FILE *inf;
356     int block_size;
357     char path[256];
358     MFile mf;
359     CFile cf;
360     int mustDisable = 0;
361     int firstTime;
362
363     if (!bfs->commit_area)
364     {
365         bf_cache (bfs, spec);
366         mustDisable = 1;
367     }
368
369     if (!(inf = open_cache (bfs, "rb")))
370         return ;
371     while (fscanf (inf, "%s %d", path, &block_size) == 2)
372     {
373         mf = mf_open (bfs->register_area, path, block_size, 0);
374         cf = cf_open (mf, bfs->commit_area, path, block_size, 1, &firstTime);
375         cf_unlink (cf);
376         cf_close (cf);
377         mf_close (mf);
378     }
379     fclose (inf);
380     unlink_cache (bfs);
381     if (mustDisable)
382         bf_cache (bfs, 0);
383 }
384
385 int bf_alloc(BFile bf, int no, zint *blocks)
386 {
387     int i;
388     assert(bf->alloc_buf);
389     bf->header_dirty = 1;
390     for (i = 0; i < no; i++)
391     {
392         if (!bf->free_list)
393             blocks[i] = bf->last_block++;
394         else
395         {
396             char buf[16];
397             const char *cp = buf;
398             memset(buf, '\0', sizeof(buf));
399
400             blocks[i] = bf->free_list;
401             if (!bf_read(bf, bf->free_list, 0, sizeof(buf), buf))
402             {
403                 yaz_log(YLOG_WARN, "Bad freelist entry " ZINT_FORMAT,
404                         bf->free_list);
405                 exit(1);
406             }
407             zebra_zint_decode(&cp, &bf->free_list);
408         }
409     }
410     return 0;
411 }
412
413 int bf_free(BFile bf, int no, const zint *blocks)
414 {
415     int i;
416     assert(bf->alloc_buf);
417     bf->header_dirty = 1;
418     for (i = 0; i < no; i++)
419     {
420         char buf[16];
421         char *cp = buf;
422         memset(buf, '\0', sizeof(buf));
423         zebra_zint_encode(&cp, bf->free_list);
424         bf->free_list = blocks[i];
425         bf_write(bf, bf->free_list, 0, sizeof(buf), buf);
426     }
427     return 0;
428 }