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