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