Changed reaction to read return values.
[idzebra-moved-to-github.git] / bfile / mfile.c
1 /*
2  * Copyright (C) 1994, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: mfile.c,v $
7  * Revision 1.3  1994-08-24 09:37:17  quinn
8  * Changed reaction to read return values.
9  *
10  * Revision 1.2  1994/08/23  14:50:48  quinn
11  * Fixed mf_close().
12  *
13  * Revision 1.1  1994/08/23  14:41:33  quinn
14  * First functional version.
15  *
16  */
17
18 #include <sys/types.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <dirent.h>
22 #include <stdlib.h>
23 #include <assert.h>
24
25 #include <util.h>
26 #include <mfile.h>
27
28 static MFile_area_struct *open_areas = 0;
29 static MFile_area_struct *default_area = 0;
30
31 static int scan_areadef(MFile_area ma, const char *name)
32 {
33     const char *ad = res_get(common_resource, name);
34     int offset = 0, rs, size, multi, rd;
35     char dirname[FILENAME_MAX+1], unit; 
36     mf_dir **dp = &ma->dirs, *dir = *dp;
37
38     if (!ad)
39     {
40         log(LOG_FATAL, "Could not find resource '%s'", name);
41         return -1;
42     }
43     for (;;)
44     {
45         rs = sscanf(ad + offset, "%[^:]:%d%c %n", dirname, &size, &unit, &rd);
46         if (rs <= 1)
47             break;
48         if (rs != 3)
49         {
50             log(LOG_FATAL, "Illegal directory description: %s", ad + offset);
51             return -1;
52         }
53         switch (unit)
54         {
55             case 'B': case 'b': multi = 1; break;
56             case 'K': case 'k': multi = 1024; break;
57             case 'M': case 'm': multi = 1048576; break;
58             default:
59                 log(LOG_FATAL, "Illegal unit: %c in %s", unit, ad + offset);
60                 return -1;
61         }
62         *dp = dir = xmalloc(sizeof(mf_dir));
63         dir->next = 0;
64         strcpy(dir->name, dirname);
65         dir->max_bytes = dir->avail_bytes = size * multi;
66         dp = &dir->next;
67         offset += rd;
68     }
69     return 0;
70 }
71
72 static int file_position(MFile mf, int pos)
73 {
74     int off = 0, c = mf->cur_file, ps;
75
76     if ((c > 0 && pos <= mf->files[c-1].top) ||
77         (c < mf->no_files -1 && pos > mf->files[c].top))
78     {
79         c = 0;
80         while (mf->files[c].top >= 0 && mf->files[c].top < pos)
81         {
82             off += mf->files[c].blocks;
83             c++;
84         }
85         assert(c < mf->no_files);
86     }
87     else
88         off = c ? (mf->files[c-1].top + 1) : 0;
89     if (mf->files[c].fd < 0 && (mf->files[c].fd = open(mf->files[c].path,
90         mf->wr ? O_RDWR|O_CREAT : O_RDONLY, 0666)) < 0)
91     {
92         log(LOG_FATAL|LOG_ERRNO, "Failed to open %s", mf->files[c].path);
93         return -1;
94     }
95     if (lseek(mf->files[c].fd, (ps = pos - off) * mf->blocksize, SEEK_SET) < 0)
96     {
97         log(LOG_FATAL|LOG_ERRNO, "Failed to seek in %s", mf->files[c].path);
98         return -1;
99     }
100     mf->cur_file = c;
101     return ps;
102 }
103
104 static int cmp_part_file(const void *p1, const void *p2)
105 {
106     return ((part_file *)p1)->number - ((part_file *)p2)->number;
107 }
108
109 /*
110  * Create a new area, cotaining metafiles in directories.
111  * Find the part-files in each directory, and inventory the existing metafiles.
112  */
113 MFile_area mf_init(const char *name)
114 {
115     MFile_area ma = xmalloc(sizeof(MFile_area_struct)), mp;
116     mf_dir *dirp;
117     meta_file *meta_f;
118     part_file *part_f;
119     DIR *dd;
120     struct dirent *dent;
121     int fd, number;
122     char metaname[FILENAME_MAX+1], tmpnam[FILENAME_MAX+1];
123
124     for (mp = open_areas; mp; mp = mp->next)
125         if (!strcmp(name, mp->name))
126             abort();
127     strcpy(ma->name, name);
128     ma->next = open_areas;
129     open_areas = ma;
130     ma->mfiles = 0;
131     ma->dirs = 0;
132     if (scan_areadef(ma, name) < 0)
133     {
134         log(LOG_FATAL, "Failed to access description of '%s'", name);
135         return 0;
136     }
137     /* look at each directory */
138     for (dirp = ma->dirs; dirp; dirp = dirp->next)
139     {
140         if (!(dd = opendir(dirp->name)))
141         {
142             log(LOG_FATAL|LOG_ERRNO, "Failed to open %s", dirp->name);
143             return 0;
144         }
145         /* look at each file */
146         while ((dent = readdir(dd)))
147         {
148             if (*dent->d_name == '.')
149                 continue;
150             if (sscanf(dent->d_name, "%[^.].%d", metaname, &number) != 2)
151             {
152                 log(LOG_FATAL, "Failed to resolve part-name %s", dent->d_name);
153                 return 0;
154             }
155             for (meta_f = ma->mfiles; meta_f; meta_f = meta_f->next)
156             {
157                 /* known metafile */
158                 if (!strcmp(meta_f->name, metaname))
159                 {
160                     part_f = &meta_f->files[meta_f->no_files++];
161                     break;
162                 }
163             }
164             /* new metafile */
165             if (!meta_f)
166             {
167                 meta_f = xmalloc(sizeof(*meta_f));
168                 meta_f->ma = ma;
169                 meta_f->next = ma->mfiles;
170                 meta_f->open = 0;
171                 meta_f->cur_file = -1;
172                 ma->mfiles = meta_f;
173                 strcpy(meta_f->name, metaname);
174                 part_f = &meta_f->files[0];
175                 meta_f->no_files = 1;
176             }
177             part_f->number = number;
178             part_f->dir = dirp;
179             part_f->fd = -1;
180             sprintf(tmpnam, "%s/%s", dirp->name, dent->d_name);
181             part_f->path = xstrdup(tmpnam);
182             /* get size */
183             if ((fd = open(part_f->path, O_RDONLY)) < 0)
184             {
185                 log(LOG_FATAL|LOG_ERRNO, "Failed to access %s", dent->d_name);
186                 return 0;
187             }
188             if ((part_f->bytes = lseek(fd, 0, SEEK_END)) < 0)
189             {
190                 log(LOG_FATAL|LOG_ERRNO, "Failed to seek in %s", dent->d_name);
191                 return 0;
192             }
193             close(fd);
194             dirp->avail_bytes -= part_f->bytes;
195         }
196         closedir(dd);
197     }
198     for (meta_f = ma->mfiles; meta_f; meta_f = meta_f->next)
199     {
200         log(LOG_DEBUG, "mf_init: %s consists of %d part(s)", meta_f->name,
201             meta_f->no_files);
202         qsort(meta_f->files, meta_f->no_files, sizeof(part_file),
203             cmp_part_file);
204     }
205     return ma;
206 }
207
208 /*
209  * Open a metafile.
210  * If !ma, Use MF_DEFAULT_AREA.
211  */
212 MFile mf_open(MFile_area ma, const char *name, int block_size, int wflag)
213 {
214     struct meta_file *new;
215     int i;
216     char tmp[FILENAME_MAX+1];
217     mf_dir *dp;
218
219     if (!ma)
220     {
221         if (!default_area && !(default_area = mf_init(MF_DEFAULT_AREA)))
222         {
223             log(LOG_FATAL, "Failed to open default area.");
224             return 0;
225         }
226         ma = default_area;
227     }
228     for (new = ma->mfiles; new; new = new->next)
229         if (!strcmp(name, new->name))
230             if (new->open)
231                 abort();
232             else
233                 break;
234     if (!new)
235     {
236         new = xmalloc(sizeof(*new));
237         strcpy(new->name, name);
238         /* allocate one, empty file */
239         new->no_files = 1;
240         new->files[0].bytes = new->files[0].blocks = 0;
241         new->files[0].top = -1;
242         new->files[0].number = 0;
243         new->files[0].fd = -1;
244         new->min_bytes_creat = MF_MIN_BLOCKS_CREAT * block_size;
245         for (dp = ma->dirs; dp && dp->avail_bytes < new->min_bytes_creat;
246             dp = dp->next);
247         if (!dp)
248         {
249             log(LOG_FATAL, "Insufficient space for new mfile.");
250             return 0;
251         }
252         new->files[0].dir = dp;
253         sprintf(tmp, "%s/%s.%d", dp->name, new->name, 0);
254         new->files[0].path = xstrdup(tmp);
255         new->ma = ma;
256     }
257     else
258     {
259         assert(!new->open);
260     }
261     new->blocksize = block_size;
262     new->min_bytes_creat = MF_MIN_BLOCKS_CREAT * block_size;
263     new->wr=wflag;
264     new->cur_file = 0;
265     new->open = 1;
266
267     for (i = 0; i < new->no_files; i++)
268     {
269         new->files[i].blocks = new->files[i].bytes / new->blocksize;
270         if (i == new->no_files)
271             new->files[i].top = -1;
272         else
273             new->files[i].top = i ? (new->files[i-1].top + new->files[i].blocks)
274                 : (new->files[i].blocks - 1);
275     }
276     return new;
277 }
278
279 /*
280  * Close a metafile.
281  */
282 int mf_close(MFile mf)
283 {
284     int i;
285
286     assert(mf->open);
287     for (i = 0; i < mf->no_files; i++)
288         if (mf->files[i].fd >= 0)
289             close(mf->files[i].fd);
290     mf->open = 0;
291     return 0;
292 }
293
294 /*
295  * Read one block from a metafile. Interface mirrors bfile.
296  */
297 int mf_read(MFile mf, int no, int offset, int num, void *buf)
298 {
299     int rd;
300
301     if (file_position(mf, no) < 0)
302         exit(1);
303     if ((rd = read(mf->files[mf->cur_file].fd, buf, mf->blocksize)) < 0)
304     {
305         log(LOG_FATAL|LOG_ERRNO, "Read failed");
306         exit(1);
307     }
308     else if (rd < mf->blocksize)
309         return 0;
310     else
311         return 1;
312 }
313
314 /*
315  * Write.
316  */
317 int mf_write(MFile mf, int no, int offset, int num, const void *buf)
318 {
319     int ps;
320     mf_dir *dp;
321     char tmp[FILENAME_MAX+1];
322
323     if ((ps = file_position(mf, no)) < 0)
324         exit(1);
325     assert(ps <= mf->files[mf->cur_file].blocks);
326     /* extend table */
327     if (ps == mf->files[mf->cur_file].blocks)
328     {
329         assert(mf->cur_file == mf->no_files - 1);
330         /* create new file */
331         if (mf->files[mf->cur_file].dir->avail_bytes <
332             mf->blocksize)
333         {
334             log(LOG_DEBUG, "Creating new file.");
335             for (dp = mf->ma->dirs; dp && dp->avail_bytes < mf->min_bytes_creat;
336                 dp = dp->next);
337             if (!dp)
338             {
339                 log(LOG_FATAL, "Cannot allocate more space for %s", mf->name);
340                 exit(1);
341             }
342             mf->files[mf->cur_file].top = no - 1;
343             mf->files[++(mf->cur_file)].top = -1;
344             mf->files[mf->cur_file].dir = dp;
345             mf->files[mf->cur_file].number =
346                 mf->files[mf->cur_file-1].number + 1;
347             mf->files[mf->cur_file].blocks =
348                 mf->files[mf->cur_file].bytes = 0;
349             mf->files[mf->cur_file].fd = -1;
350             sprintf(tmp, "%s/%s.%d", dp->name, mf->name,
351                 mf->files[mf->cur_file].number);
352             mf->files[mf->cur_file].path = xstrdup(tmp);
353             mf->no_files++;
354             /* open new file and position at beginning */
355             if (file_position(mf, no) < 0)
356                 exit(1);
357         }
358         mf->files[mf->cur_file].blocks++;
359         mf->files[mf->cur_file].bytes += mf->blocksize;
360         mf->files[mf->cur_file].dir->avail_bytes -= mf->blocksize;
361     }
362     if (write(mf->files[mf->cur_file].fd, buf, mf->blocksize) < mf->blocksize)
363     {
364         log(LOG_FATAL|LOG_ERRNO, "Write failed");
365         exit(1);
366     }
367     return 0;
368 }
369
370 /*
371  * Destroy a metafile, unlinking component files. File must be open.
372  */
373 int mf_unlink(MFile mf)
374 {
375     abort();
376     return 0;
377 }
378
379 /*
380  * Unlink the file by name, rather than MFile-handle. File should be closed.
381  */
382 int mf_unlink_name(MFile_area ma, const char *name)
383 {
384     abort();
385     return 0;
386 }