IDZEBRA 2.2.8
mfile.c
Go to the documentation of this file.
1/* This file is part of the Zebra server.
2 Copyright (C) Index Data
3
4Zebra is free software; you can redistribute it and/or modify it under
5the terms of the GNU General Public License as published by the Free
6Software Foundation; either version 2, or (at your option) any later
7version.
8
9Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10WARRANTY; without even the implied warranty of MERCHANTABILITY or
11FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program; if not, write to the Free Software
16Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
18*/
19
20#if HAVE_CONFIG_H
21#include <config.h>
22#endif
23#include <sys/types.h>
24#include <fcntl.h>
25#ifdef WIN32
26#include <io.h>
27#endif
28#if HAVE_UNISTD_H
29#include <unistd.h>
30#endif
31#include <direntz.h>
32#include <string.h>
33#include <stdlib.h>
34#include <stdio.h>
35#include <assert.h>
36#include <errno.h>
37
38#include <zebra-lock.h>
39#include <idzebra/util.h>
40#include <yaz/yaz-util.h>
41#include <yaz/snprintf.h>
42#include "mfile.h"
43
44static int scan_areadef(MFile_area ma, const char *ad, const char *base)
45{
46 /*
47 * If no definition is given, use current directory, unlimited.
48 */
49 char dirname[FILENAME_MAX+1];
50 mf_dir **dp = &ma->dirs, *dir = *dp;
51
52 if (!ad)
53 ad = ".:-1b";
54 for (;;)
55 {
56 const char *ad0 = ad;
57 int i = 0, fact = 1, multi;
58 mfile_off_t size = 0;
59
60 while (*ad == ' ' || *ad == '\t')
61 ad++;
62 if (!*ad)
63 break;
64 if (!yaz_is_abspath(ad) && base)
65 {
66 strcpy(dirname, base);
67 i = strlen(dirname);
68 dirname[i++] = '/';
69 }
70 while (*ad)
71 {
72 if (*ad == ':' && strchr("+-0123456789", ad[1]))
73 break;
74 if (i < FILENAME_MAX)
75 dirname[i++] = *ad;
76 ad++;
77 }
78 dirname[i] = '\0';
79 if (*ad++ != ':')
80 {
81 yaz_log(YLOG_WARN, "Missing colon after path: %s", ad0);
82 return -1;
83 }
84 if (i == 0)
85 {
86 yaz_log(YLOG_WARN, "Empty path: %s", ad0);
87 return -1;
88 }
89 while (*ad == ' ' || *ad == '\t')
90 ad++;
91 if (*ad == '-')
92 {
93 fact = -1;
94 ad++;
95 }
96 else if (*ad == '+')
97 ad++;
98 size = 0;
99 if (*ad < '0' || *ad > '9')
100 {
101 yaz_log(YLOG_FATAL, "Missing size after path: %s", ad0);
102 return -1;
103 }
104 size = 0;
105 while (*ad >= '0' && *ad <= '9')
106 size = size*10 + (*ad++ - '0');
107 switch (*ad)
108 {
109 case 'B': case 'b': multi = 1; break;
110 case 'K': case 'k': multi = 1024; break;
111 case 'M': case 'm': multi = 1048576; break;
112 case 'G': case 'g': multi = 1073741824; break;
113 case '\0':
114 yaz_log(YLOG_FATAL, "Missing unit: %s", ad0);
115 return -1;
116 default:
117 yaz_log(YLOG_FATAL, "Illegal unit: %c in %s", *ad, ad0);
118 return -1;
119 }
120 ad++;
121 *dp = dir = (mf_dir *) xmalloc(sizeof(mf_dir));
122 dir->next = 0;
123 strcpy(dir->name, dirname);
124 dir->max_bytes = dir->avail_bytes = fact * size * multi;
125 dp = &dir->next;
126 }
127 return 0;
128}
129
139static zint file_position(MFile mf, zint pos, int offset)
140{
141 zint off = 0, ps;
142 int c = mf->cur_file;
143
144 if ((c > 0 && pos <= mf->files[c-1].top) ||
145 (c < mf->no_files -1 && pos > mf->files[c].top))
146 {
147 c = 0;
148 while (c + 1 < mf->no_files && mf->files[c].top < pos)
149 {
150 off += mf->files[c].blocks;
151 c++;
152 }
153 assert(c < mf->no_files);
154 }
155 else
156 off = c ? (mf->files[c-1].top + 1) : 0;
157 if (mf->files[c].fd < 0)
158 {
159 if ((mf->files[c].fd = open(mf->files[c].path,
160 mf->wr ?
161 (O_BINARY|O_RDWR|O_CREAT) :
162 (O_BINARY|O_RDONLY), 0666)) < 0)
163 {
164 if (!mf->wr && errno == ENOENT && off == 0)
165 {
166 /* we can't open it for reading. But not really an error */
167 return -2;
168 }
169 yaz_log(YLOG_WARN|YLOG_ERRNO, "Failed to open %s", mf->files[c].path);
170 return -1;
171 }
172 }
173 ps = pos - off;
174 if (mfile_seek(mf->files[c].fd, ps *(mfile_off_t) mf->blocksize + offset,
175 SEEK_SET) < 0)
176 {
177 yaz_log(YLOG_WARN|YLOG_ERRNO, "Failed to seek in %s", mf->files[c].path);
178 yaz_log(YLOG_WARN, "pos=" ZINT_FORMAT " off=" ZINT_FORMAT " blocksize=%d offset=%d",
179 pos, off, mf->blocksize, offset);
180 return -1;
181 }
182 mf->cur_file = c;
183 return ps;
184}
185
186static int cmp_part_file(const void *p1, const void *p2)
187{
188 zint d = ((part_file *)p1)->number - ((part_file *)p2)->number;
189 if (d > 0)
190 return 1;
191 if (d < 0)
192 return -1;
193 return 0;
194}
195
196MFile_area mf_init(const char *name, const char *spec, const char *base,
197 int only_shadow_files)
198{
199 MFile_area ma = (MFile_area) xmalloc(sizeof(*ma));
200 mf_dir *dirp;
201 meta_file *meta_f;
202 part_file *part_f = 0;
203 DIR *dd;
204 struct dirent *dent;
205 int fd, number;
206 char metaname[FILENAME_MAX+1], tmpnam[FILENAME_MAX+1];
207
208 yaz_log(YLOG_DEBUG, "mf_init(%s)", name);
209 strcpy(ma->name, name);
210 ma->mfiles = 0;
211 ma->dirs = 0;
212 if (scan_areadef(ma, spec, base) < 0)
213 {
214 yaz_log(YLOG_WARN, "Failed to access description of '%s'", name);
215 mf_destroy(ma);
216 return 0;
217 }
218 /* look at each directory */
219 for (dirp = ma->dirs; dirp; dirp = dirp->next)
220 {
221 if (!(dd = opendir(dirp->name)))
222 {
223 yaz_log(YLOG_WARN|YLOG_ERRNO, "Failed to open directory %s",
224 dirp->name);
225 mf_destroy(ma);
226 return 0;
227 }
228 /* look at each file */
229 while ((dent = readdir(dd)))
230 {
231 int len = strlen(dent->d_name);
232 const char *cp = strrchr(dent->d_name, '-');
233 if (strchr(".-", *dent->d_name))
234 continue;
235 if (len < 5 || !cp || strcmp(dent->d_name + len - 3, ".mf"))
236 continue;
237 number = atoi(cp+1);
238 memcpy(metaname, dent->d_name, cp - dent->d_name);
239 metaname[ cp - dent->d_name] = '\0';
240
241 /* only files such as file-i-0.mf and file-i-b-0.mf, bug #739 */
242 if (only_shadow_files && cp[-2] != '-')
243 continue;
244 if (!only_shadow_files && cp[-2] == '-')
245 continue;
246 for (meta_f = ma->mfiles; meta_f; meta_f = meta_f->next)
247 {
248 /* known metafile */
249 if (!strcmp(meta_f->name, metaname))
250 {
251 part_f = &meta_f->files[meta_f->no_files++];
252 break;
253 }
254 }
255 /* new metafile */
256 if (!meta_f)
257 {
258 meta_f = (meta_file *) xmalloc(sizeof(*meta_f));
259 zebra_mutex_init(&meta_f->mutex);
260 meta_f->ma = ma;
261 meta_f->next = ma->mfiles;
262 meta_f->open = 0;
263 meta_f->cur_file = -1;
264 ma->mfiles = meta_f;
265 strcpy(meta_f->name, metaname);
266 part_f = &meta_f->files[0];
267 meta_f->no_files = 1;
268 }
269 part_f->number = number;
270 part_f->dir = dirp;
271 part_f->fd = -1;
272 yaz_snprintf(tmpnam, FILENAME_MAX,
273 "%s/%s", dirp->name, dent->d_name);
274 part_f->path = xstrdup(tmpnam);
275 /* get size */
276 if ((fd = open(part_f->path, O_BINARY|O_RDONLY)) < 0)
277 {
278 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to access %s",
279 dent->d_name);
280 closedir(dd);
281 mf_destroy(ma);
282 return 0;
283 }
284 if ((part_f->bytes = mfile_seek(fd, 0, SEEK_END)) < 0)
285 {
286 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to seek in %s",
287 dent->d_name);
288 close(fd);
289 closedir(dd);
290 mf_destroy(ma);
291 return 0;
292 }
293 close(fd);
294 if (dirp->max_bytes >= 0)
295 dirp->avail_bytes -= part_f->bytes;
296 }
297 closedir(dd);
298 }
299 for (meta_f = ma->mfiles; meta_f; meta_f = meta_f->next)
300 {
301 yaz_log(YLOG_DEBUG, "mf_init: %s consists of %d part(s)", meta_f->name,
302 meta_f->no_files);
303 qsort(meta_f->files, meta_f->no_files, sizeof(part_file),
305 }
306 return ma;
307}
308
310{
311 mf_dir *dp;
312
313 if (!ma)
314 return;
315 dp = ma->dirs;
316 while (dp)
317 {
318 mf_dir *d = dp;
319 dp = dp->next;
320 xfree(d);
321 }
322 mf_reset(ma, 0);
323 xfree(ma);
324}
325
326void mf_reset(MFile_area ma, int unlink_flag)
327{
328 meta_file *meta_f;
329
330 if (!ma)
331 return;
332 meta_f = ma->mfiles;
333 while (meta_f)
334 {
335 int i;
336 meta_file *m = meta_f;
337
338 meta_f = meta_f->next;
339
340 assert(!m->open);
341 for (i = 0; i<m->no_files; i++)
342 {
343 if (unlink_flag)
344 unlink(m->files[i].path);
345 xfree(m->files[i].path);
346 }
348 xfree(m);
349 }
350 ma->mfiles = 0;
351}
352
353MFile mf_open(MFile_area ma, const char *name, int block_size, int wflag)
354{
355 meta_file *mnew;
356 int i;
357 char tmp[FILENAME_MAX+1];
358 mf_dir *dp;
359
360 yaz_log(YLOG_DEBUG, "mf_open(%s bs=%d, %s)", name, block_size,
361 wflag ? "RW" : "RDONLY");
362 assert(ma);
363 for (mnew = ma->mfiles; mnew; mnew = mnew->next)
364 if (!strcmp(name, mnew->name))
365 {
366 if (mnew->open)
367 {
368 yaz_log(YLOG_WARN, "metafile %s already open", name);
369 return 0;
370 }
371 break;
372 }
373 if (!mnew)
374 {
375 mnew = (meta_file *) xmalloc(sizeof(*mnew));
376 strcpy(mnew->name, name);
377 /* allocate one, empty file */
378 zebra_mutex_init(&mnew->mutex);
379 mnew->no_files = 1;
380 mnew->files[0].bytes = 0;
381 mnew->files[0].blocks = 0;
382 mnew->files[0].top = -1;
383 mnew->files[0].number = 0;
384 mnew->files[0].fd = -1;
385 mnew->min_bytes_creat = MF_MIN_BLOCKS_CREAT * block_size;
386 for (dp = ma->dirs; dp && dp->max_bytes >= 0 && dp->avail_bytes <
387 mnew->min_bytes_creat; dp = dp->next);
388 if (!dp)
389 {
390 yaz_log(YLOG_FATAL, "Insufficient space for file %s", name);
391 xfree(mnew);
392 return 0;
393 }
394 mnew->files[0].dir = dp;
395 yaz_snprintf(tmp, FILENAME_MAX, "%s/%s-%d.mf", dp->name, mnew->name, 0);
396 mnew->files[0].path = xstrdup(tmp);
397 mnew->ma = ma;
398 mnew->next = ma->mfiles;
399 ma->mfiles = mnew;
400 }
401 else
402 {
403 for (i = 0; i < mnew->no_files; i++)
404 {
405 if (mnew->files[i].bytes % block_size)
406 mnew->files[i].bytes += block_size - mnew->files[i].bytes %
407 block_size;
408 mnew->files[i].blocks = (int) (mnew->files[i].bytes / block_size);
409 }
410 assert(!mnew->open);
411 }
412 mnew->blocksize = block_size;
413 mnew->min_bytes_creat = MF_MIN_BLOCKS_CREAT * block_size;
414 mnew->wr=wflag;
415 mnew->cur_file = 0;
416 mnew->open = 1;
417
418 for (i = 0; i < mnew->no_files; i++)
419 {
420 mnew->files[i].blocks = (int)(mnew->files[i].bytes / mnew->blocksize);
421 if (i == mnew->no_files - 1)
422 mnew->files[i].top = -1;
423 else
424 mnew->files[i].top =
425 i ? (mnew->files[i-1].top + mnew->files[i].blocks)
426 : (mnew->files[i].blocks - 1);
427 }
428 return mnew;
429}
430
432{
433 int i;
434
435 yaz_log(YLOG_DEBUG, "mf_close(%s)", mf->name);
436 assert(mf->open);
437 for (i = 0; i < mf->no_files; i++)
438 {
439 if (mf->files[i].fd >= 0)
440 {
441#ifndef WIN32
442 if (mf->wr)
443 fsync(mf->files[i].fd);
444#endif
445 close(mf->files[i].fd);
446 mf->files[i].fd = -1;
447 }
448 }
449 mf->open = 0;
450 return 0;
451}
452
453int mf_read(MFile mf, zint no, int offset, int nbytes, void *buf)
454{
455 zint rd;
456 int toread;
457
459 if ((rd = file_position(mf, no, offset)) < 0)
460 {
461 if (rd == -2)
462 {
464 return 0;
465 }
466 else
467 {
468 yaz_log(YLOG_FATAL, "mf_read2 %s internal error", mf->name);
469 return -1;
470 }
471 }
472 toread = nbytes ? nbytes : mf->blocksize;
473 if ((rd = read(mf->files[mf->cur_file].fd, buf, toread)) < 0)
474 {
475 yaz_log(YLOG_FATAL|YLOG_ERRNO, "mf_read2: Read failed (%s)",
476 mf->files[mf->cur_file].path);
477 return -1;
478 }
480 if (rd < toread)
481 return 0;
482 else
483 return 1;
484}
485
486int mf_write(MFile mf, zint no, int offset, int nbytes, const void *buf)
487{
488 int ret = 0;
489 zint ps;
490 zint nblocks;
491 int towrite;
492 mf_dir *dp;
493 char tmp[FILENAME_MAX+1];
494 unsigned char dummych = '\xff';
495
497 if ((ps = file_position(mf, no, offset)) < 0)
498 {
499 yaz_log(YLOG_FATAL, "mf_write: %s error (1)", mf->name);
500 ret = -1;
501 goto out;
502 }
503 /* file needs to grow */
504 while (ps >= mf->files[mf->cur_file].blocks)
505 {
506 mfile_off_t needed = (ps - mf->files[mf->cur_file].blocks + 1) *
507 mf->blocksize;
508 /* file overflow - allocate new file */
509 if (mf->files[mf->cur_file].dir->max_bytes >= 0 &&
510 needed > mf->files[mf->cur_file].dir->avail_bytes)
511 {
512 /* cap off file? */
513 if ((nblocks = (int) (mf->files[mf->cur_file].dir->avail_bytes /
514 mf->blocksize)) > 0)
515 {
516 yaz_log(YLOG_DEBUG, "Capping off file %s at pos " ZINT_FORMAT,
517 mf->files[mf->cur_file].path, nblocks);
518 if ((ps = file_position(mf,
519 (mf->cur_file ? mf->files[mf->cur_file-1].top : 0) +
520 mf->files[mf->cur_file].blocks + nblocks - 1, 0)) < 0)
521 {
522 yaz_log(YLOG_FATAL, "mf_write: %s error (2)",
523 mf->name);
524 ret = -1;
525 goto out;
526 }
527 yaz_log(YLOG_DEBUG, "ps = " ZINT_FORMAT, ps);
528 if (write(mf->files[mf->cur_file].fd, &dummych, 1) < 1)
529 {
530 yaz_log(YLOG_ERRNO|YLOG_FATAL, "mf_write: %s error (3)",
531 mf->name);
532 ret = -1;
533 goto out;
534 }
535 mf->files[mf->cur_file].blocks += nblocks;
536 mf->files[mf->cur_file].bytes += nblocks * mf->blocksize;
537 mf->files[mf->cur_file].dir->avail_bytes -= nblocks *
538 mf->blocksize;
539 }
540 /* get other bit */
541 yaz_log(YLOG_DEBUG, "Creating new file.");
542 for (dp = mf->ma->dirs; dp && dp->max_bytes >= 0 &&
543 dp->avail_bytes < needed; dp = dp->next);
544 if (!dp)
545 {
546 yaz_log(YLOG_FATAL, "mf_write: %s error (4) no more space",
547 mf->name);
548 for (dp = mf->ma->dirs; dp ; dp = dp->next) {
549 yaz_log(YLOG_FATAL,"%s: max=" ZINT_FORMAT
550 " used=" ZINT_FORMAT " available=" ZINT_FORMAT,
551 dp->name, (zint)dp->max_bytes,
552 (zint)(dp->max_bytes - dp->avail_bytes), (zint)dp->avail_bytes );
553 }
554 yaz_log(YLOG_FATAL,"Adjust the limits in your zebra.cfg");
555 ret = -1;
556 goto out;
557 }
558 mf->files[mf->cur_file].top = (mf->cur_file ?
559 mf->files[mf->cur_file-1].top : -1) +
560 mf->files[mf->cur_file].blocks;
561 mf->files[++(mf->cur_file)].top = -1;
562 mf->files[mf->cur_file].dir = dp;
563 mf->files[mf->cur_file].number =
564 mf->files[mf->cur_file-1].number + 1;
565 mf->files[mf->cur_file].blocks = 0;
566 mf->files[mf->cur_file].bytes = 0;
567 mf->files[mf->cur_file].fd = -1;
568 yaz_snprintf(tmp, FILENAME_MAX,
569 "%s/%s-" ZINT_FORMAT ".mf", dp->name, mf->name,
570 mf->files[mf->cur_file].number);
571 mf->files[mf->cur_file].path = xstrdup(tmp);
572 mf->no_files++;
573 /* open new file and position at beginning */
574 if ((ps = file_position(mf, no, offset)) < 0)
575 {
576 yaz_log(YLOG_FATAL, "mf_write: %s error (5)", mf->name);
577 ret = -1;
578 goto out;
579 }
580 }
581 else
582 {
583 nblocks = ps - mf->files[mf->cur_file].blocks + 1;
584 mf->files[mf->cur_file].blocks += nblocks;
585 mf->files[mf->cur_file].bytes += nblocks * mf->blocksize;
586 if (mf->files[mf->cur_file].dir->max_bytes >= 0)
587 mf->files[mf->cur_file].dir->avail_bytes -=
588 nblocks * mf->blocksize;
589 }
590 }
591 towrite = nbytes ? nbytes : mf->blocksize;
592 if (write(mf->files[mf->cur_file].fd, buf, towrite) < towrite)
593 {
594 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Write failed for file %s part %d",
595 mf->name, mf->cur_file);
596 ret = -1;
597 }
598 out:
600 return ret;
601}
602
612int mf_area_directory_stat(MFile_area ma, int no, const char **directory,
613 double *used_bytes, double *max_bytes)
614{
615 int i;
616 mf_dir *d = ma->dirs;
617 for (i = 0; d && i<no; i++, d = d->next)
618 ;
619 if (!d)
620 return 0;
621 if (directory)
622 *directory = d->name;
623 if (max_bytes)
624 {
625 /* possible loss of data. But it's just statistics and lies */
626 *max_bytes = (double) d->max_bytes;
627 }
628 if (used_bytes)
629 {
630 /* possible loss of data. But it's just statistics and lies */
631 *used_bytes = (double) (d->max_bytes - d->avail_bytes);
632 }
633 return 1;
634}
635/*
636 * Local variables:
637 * c-basic-offset: 4
638 * c-file-style: "Stroustrup"
639 * indent-tabs-mode: nil
640 * End:
641 * vim: shiftwidth=4 tabstop=8 expandtab
642 */
643
#define O_BINARY
Definition agrep.c:46
static int cmp_part_file(const void *p1, const void *p2)
Definition mfile.c:186
int mf_write(MFile mf, zint no, int offset, int nbytes, const void *buf)
writes block to metafile
Definition mfile.c:486
int mf_area_directory_stat(MFile_area ma, int no, const char **directory, double *used_bytes, double *max_bytes)
metafile area statistics
Definition mfile.c:612
static zint file_position(MFile mf, zint pos, int offset)
position within metafile (perform seek)
Definition mfile.c:139
int mf_read(MFile mf, zint no, int offset, int nbytes, void *buf)
reads block from metafile
Definition mfile.c:453
void mf_reset(MFile_area ma, int unlink_flag)
reset all files in a metafile area (optionally delete them as well)
Definition mfile.c:326
void mf_destroy(MFile_area ma)
destroys metafile area handle
Definition mfile.c:309
MFile_area mf_init(const char *name, const char *spec, const char *base, int only_shadow_files)
creates a metafile area
Definition mfile.c:196
int mf_close(MFile mf)
closes metafile
Definition mfile.c:431
MFile mf_open(MFile_area ma, const char *name, int block_size, int wflag)
opens metafile
Definition mfile.c:353
static int scan_areadef(MFile_area ma, const char *ad, const char *base)
Definition mfile.c:44
#define mfile_seek
Definition mfile.h:37
#define MF_MIN_BLOCKS_CREAT
Definition mfile.h:49
struct MFile_area_struct * MFile_area
Definition mfile.h:75
off_t mfile_off_t
Definition mfile.h:36
#define FILENAME_MAX
Definition mfile.h:42
struct meta_file * mfiles
Definition mfile.h:97
mf_dir * dirs
Definition mfile.h:96
char name[FILENAME_MAX+1]
Definition mfile.h:95
int wr
Definition mfile.h:87
MFile_area ma
Definition mfile.h:86
struct meta_file * next
Definition mfile.h:90
int open
Definition mfile.h:83
int blocksize
Definition mfile.h:84
char name[FILENAME_MAX+1]
Definition mfile.h:79
mfile_off_t min_bytes_creat
Definition mfile.h:85
int no_files
Definition mfile.h:81
part_file files[MF_MAX_PARTS]
Definition mfile.h:80
int cur_file
Definition mfile.h:82
Zebra_mutex mutex
Definition mfile.h:88
Definition mfile.h:56
mfile_off_t avail_bytes
Definition mfile.h:59
mfile_off_t max_bytes
Definition mfile.h:58
struct mf_dir * next
Definition mfile.h:60
char name[FILENAME_MAX+1]
Definition mfile.h:57
char * path
Definition mfile.h:70
mf_dir * dir
Definition mfile.h:69
zint number
Definition mfile.h:65
zint top
Definition mfile.h:66
zint blocks
Definition mfile.h:67
mfile_off_t bytes
Definition mfile.h:68
int fd
Definition mfile.h:71
int fd
long zint
Zebra integer.
Definition util.h:66
#define ZINT_FORMAT
Definition util.h:72
int zebra_mutex_unlock(Zebra_mutex *p)
Definition zebra-lock.c:74
int zebra_mutex_init(Zebra_mutex *p)
Definition zebra-lock.c:31
int zebra_mutex_lock(Zebra_mutex *p)
Definition zebra-lock.c:59
int zebra_mutex_destroy(Zebra_mutex *p)
Definition zebra-lock.c:43