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