]> git.pond.sub.org Git - empserver/blob - src/lib/common/file.c
Clean up initialization of empfile[]
[empserver] / src / lib / common / file.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  file.c: Operations on Empire tables (`files' for historical reasons)
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1989
32  *     Steve McClure, 2000
33  *     Markus Armbruster, 2005-2006
34  */
35
36 #include <config.h>
37
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43 #include "file.h"
44 #include "match.h"
45 #include "misc.h"
46 #include "nsc.h"
47 #include "prototypes.h"
48
49 static int fillcache(struct empfile *, int);
50 static int do_write(struct empfile *, void *, int, int);
51
52 /*
53  * Open the file-backed table TYPE (EF_SECTOR, ...).
54  * HOW are flags to control operation.  Naturally, immutable flags are
55  * not permitted.
56  * Return non-zero on success, zero on failure.
57  * You must call ef_close() before the next ef_open().
58  */
59 int
60 ef_open(int type, int how)
61 {
62     struct empfile *ep;
63     struct flock lock;
64     int oflags, fd, fsiz, size;
65
66     if (ef_check(type) < 0)
67         return 0;
68     if (CANT_HAPPEN(how & EFF_IMMUTABLE))
69         how &= ~EFF_IMMUTABLE;
70
71     /* open file */
72     ep = &empfile[type];
73     if (CANT_HAPPEN(ep->fd >= 0))
74         return 0;
75     oflags = O_RDWR;
76     if (how & EFF_RDONLY)
77         oflags = O_RDONLY;
78     if (how & EFF_CREATE)
79         oflags |= O_CREAT | O_TRUNC;
80 #if defined(_WIN32)
81     oflags |= O_BINARY;
82 #endif
83     if ((fd = open(ep->file, oflags, S_IRWUG)) < 0) {
84         logerror("Can't open %s (%s)", ep->file, strerror(errno));
85         return 0;
86     }
87
88     lock.l_type = how & EFF_RDONLY ? F_RDLCK : F_WRLCK;
89     lock.l_whence = SEEK_SET;
90     lock.l_start = lock.l_len = 0;
91     if (fcntl(fd, F_SETLK, &lock) == -1) {
92         logerror("Can't lock %s (%s)", ep->file, strerror(errno));
93         close(fd);
94         return 0;
95     }
96
97     /* get file size */
98     fsiz = fsize(fd);
99     if (fsiz % ep->size) {
100         logerror("Can't open %s (file size not a multiple of record size %d)",
101                  ep->file, ep->size);
102         close(fd);
103         return 0;
104     }
105     ep->fids = fsiz / ep->size;
106
107     /* allocate cache */
108     if (ep->flags & EFF_STATIC) {
109         /* ep->cache already points to space for ep->csize elements */
110         if (how & EFF_MEM) {
111             if (ep->fids > ep->csize) {
112                 logerror("Can't open %s: file larger than %d bytes",
113                          ep->file, ep->fids * ep->size);
114                 close(fd);
115                 return 0;
116             }
117         }
118     } else {
119         if (how & EFF_MEM)
120             ep->csize = ep->fids;
121         else
122             ep->csize = blksize(fd) / ep->size;
123         /* 0 could lead to null cache, which confuses assertions */
124         if (!ep->csize)
125             ep->csize++;
126         size = ep->csize * ep->size;
127         if (CANT_HAPPEN(ep->cache))
128             free(ep->cache);
129         ep->cache = malloc(size);
130         if (ep->cache == NULL) {
131             logerror("Can't open %s: out of memory", ep->file);
132             close(fd);
133             return 0;
134         }
135     }
136     ep->baseid = 0;
137     ep->cids = 0;
138     ep->flags = (ep->flags & EFF_IMMUTABLE) | (how & ~EFF_CREATE);
139     ep->fd = fd;
140
141     /* map file into cache */
142     if ((how & EFF_MEM) && ep->fids) {
143         if (fillcache(ep, 0) != ep->fids) {
144             ep->cids = 0;       /* prevent cache flush */
145             ep->flags &= EFF_IMMUTABLE; /* maintain invariant */
146             ef_close(type);
147             return 0;
148         }
149     }
150
151     /*
152      * Could close fd if both EFF_RDONLY and EFF_MEM, but that doesn't
153      * happen, so don't bother.
154      */
155
156     return 1;
157 }
158
159 /*
160  * Close the file-backed table TYPE (EF_SECTOR, ...).
161  * Return non-zero on success, zero on failure.
162  */
163 int
164 ef_close(int type)
165 {
166     struct empfile *ep;
167     int retval;
168
169     retval = ef_flush(type);
170     ep = &empfile[type];
171     ep->flags &= EFF_IMMUTABLE;
172     if (!(ep->flags & EFF_STATIC)) {
173         free(ep->cache);
174         ep->cache = NULL;
175     }
176     if (close(ep->fd) < 0) {
177         logerror("Error closing %s (%s)", ep->name, strerror(errno));
178         retval = 0;
179     }
180     ep->fd = -1;
181     return retval;
182 }
183
184 /*
185  * Flush file-backed table TYPE (EF_SECTOR, ...) to disk.
186  * Return non-zero on success, zero on failure.
187  */
188 int
189 ef_flush(int type)
190 {
191     struct empfile *ep;
192
193     if (ef_check(type) < 0)
194         return 0;
195     ep = &empfile[type];
196     if (CANT_HAPPEN(ep->fd < 0))
197         return 0;
198     /*
199      * We don't know which cache entries are dirty.  ef_write() writes
200      * through, but direct updates through ef_ptr() don't.  They are
201      * allowed only with EFF_MEM.  Assume the whole cash is dirty
202      * then.
203      */
204     if (!(ep->flags & EFF_RDONLY) && (ep->flags & EFF_MEM))
205         return do_write(ep, ep->cache, ep->baseid, ep->cids) >= 0;
206
207     return 1;
208 }
209
210 /*
211  * Return pointer to element ID in table TYPE if it exists, else NULL.
212  * The table must be fully cached, i.e. flags & EFF_MEM.
213  * The caller is responsible for flushing changes he makes.
214  */
215 void *
216 ef_ptr(int type, int id)
217 {
218     struct empfile *ep;
219
220     if (ef_check(type) < 0)
221         return NULL;
222     ep = &empfile[type];
223     if (CANT_HAPPEN(!(ep->flags & EFF_MEM) || !ep->cache))
224         return NULL;
225     if (id < 0 || id >= ep->fids)
226         return NULL;
227     return ep->cache + ep->size * id;
228 }
229
230 /*
231  * Read element ID from table TYPE into buffer INTO.
232  * FIXME pass buffer size!
233  * Return non-zero on success, zero on failure.
234  */
235 int
236 ef_read(int type, int id, void *into)
237 {
238     struct empfile *ep;
239     void *from;
240
241     if (ef_check(type) < 0)
242         return 0;
243     ep = &empfile[type];
244     if (CANT_HAPPEN(!ep->cache))
245         return 0;
246     if (id < 0 || id >= ep->fids)
247         return 0;
248
249     if (ep->flags & EFF_MEM) {
250         from = ep->cache + id * ep->size;
251     } else {
252         if (ep->baseid + ep->cids <= id || ep->baseid > id) {
253             if (fillcache(ep, id) < 1)
254                 return 0;
255         }
256         from = ep->cache + (id - ep->baseid) * ep->size;
257     }
258     memcpy(into, from, ep->size);
259
260     if (ep->postread)
261         ep->postread(id, into);
262     return 1;
263 }
264
265 /*
266  * Fill cache of EP with elements starting at ID.
267  * If any were read, return their number.
268  * Else return -1 and leave the cache unchanged.
269  */
270 static int
271 fillcache(struct empfile *ep, int start)
272 {
273     int n, ret;
274     char *p;
275
276     if (CANT_HAPPEN(ep->fd < 0 || !ep->cache))
277         return -1;
278
279     if (lseek(ep->fd, start * ep->size, SEEK_SET) == (off_t)-1) {
280         logerror("Error seeking %s (%s)", ep->file, strerror(errno));
281         return -1;
282     }
283
284     p = ep->cache;
285     n = ep->csize * ep->size;
286     while (n > 0) {
287         ret = read(ep->fd, p, n);
288         if (ret < 0) {
289             if (errno != EAGAIN) {
290                 logerror("Error reading %s (%s)", ep->file, strerror(errno));
291                 break;
292             }
293         } else if (ret == 0) {
294             break;
295         } else {
296             p += ret;
297             n -= ret;
298         }
299     }
300
301     if (p == ep->cache)
302         return -1;              /* nothing read, old cache still ok */
303
304     ep->baseid = start;
305     ep->cids = (p - ep->cache) / ep->size;
306     return ep->cids;
307 }
308
309 /*
310  * Write COUNT elements from BUF to EP, starting at ID.
311  * Return 0 on success, -1 on error.
312  */
313 static int
314 do_write(struct empfile *ep, void *buf, int id, int count)
315 {
316     int n, ret;
317     char *p;
318
319     if (CANT_HAPPEN(ep->fd < 0 || id < 0 || count < 0))
320         return -1;
321
322     if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) {
323         logerror("Error seeking %s (%s)", ep->file, strerror(errno));
324         return -1;
325     }
326
327     p = buf;
328     n = count * ep->size;
329     while (n > 0) {
330         ret = write(ep->fd, p, n);
331         if (ret < 0) {
332             if (errno != EAGAIN) {
333                 logerror("Error writing %s (%s)", ep->file, strerror(errno));
334                 /* FIXME if this extended file, truncate back to old size */
335                 return -1;
336             }
337         } else {
338             p += ret;
339             n -= ret;
340         }
341     }
342
343     return 0;
344 }
345
346 /*
347  * Write element ID into file-backed table TYPE from buffer FROM.
348  * FIXME pass buffer size!
349  * Write through cache straight to disk.
350  * Cannot write beyond the end of fully cached table (flags & EFF_MEM).
351  * Can write at the end of partially cached table.
352  * Return non-zero on success, zero on failure.
353  */
354 int
355 ef_write(int type, int id, void *from)
356 {
357     struct empfile *ep;
358     char *to;
359
360     if (ef_check(type) < 0)
361         return 0;
362     ep = &empfile[type];
363     if (ep->prewrite)
364         ep->prewrite(id, from);
365     if (CANT_HAPPEN((ep->flags & EFF_MEM) ? id >= ep->fids : id > ep->fids))
366         return 0;               /* not implemented */
367     if (do_write(ep, from, id, 1) < 0)
368         return 0;
369     if (id >= ep->baseid && id < ep->baseid + ep->cids) {
370         /* update the cache if necessary */
371         to = ep->cache + (id - ep->baseid) * ep->size;
372         if (to != from)
373             memcpy(to, from, ep->size);
374     }
375     if (id >= ep->fids) {
376         /* write beyond end of file extends it, take note */
377         ep->fids = id + 1;
378     }
379     return 1;
380 }
381
382 /*
383  * Extend the file-backed table TYPE by COUNT elements.
384  * Return non-zero on success, zero on failure.
385  */
386 int
387 ef_extend(int type, int count)
388 {
389     struct empfile *ep;
390     void *tmpobj;
391     int id, i, how;
392
393     if (ef_check(type) < 0)
394         return 0;
395     ep = &empfile[type];
396     if (CANT_HAPPEN(ep->fd < 0 || count < 0))
397         return 0;
398
399     tmpobj = calloc(1, ep->size);
400     id = ep->fids;
401     for (i = 0; i < count; i++) {
402         if (ep->init)
403             ep->init(id + i, tmpobj);
404         if (do_write(ep, tmpobj, id + i, 1) < 0)
405             break;
406     }
407     free(tmpobj);
408
409     if (ep->flags & EFF_MEM) {
410         /* FIXME lazy bastards...  do this right */
411         /* XXX this will cause problems if there are ef_ptrs (to the
412          * old allocated structure) active when we do the re-open */
413         how = ep->flags & ~EFF_IMMUTABLE;
414         ef_close(type);
415         ef_open(type, how);
416     } else {
417         ep->fids += i;
418     }
419
420     return i == count;
421 }
422
423 struct castr *
424 ef_cadef(int type)
425 {
426     return empfile[type].cadef;
427 }
428
429 int
430 ef_nelem(int type)
431 {
432     return empfile[type].fids;
433 }
434
435 int
436 ef_flags(int type)
437 {
438     return empfile[type].flags;
439 }
440
441 time_t
442 ef_mtime(int type)
443 {
444     if (empfile[type].fd <= 0)
445         return 0;
446     return fdate(empfile[type].fd);
447 }
448
449 /*
450  * Search for a table matching NAME, return its table type.
451  * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
452  * several.
453  */
454 int
455 ef_byname(char *name)
456 {
457     return stmtch(name, empfile, offsetof(struct empfile, name),
458                   sizeof(empfile[0]));
459 }
460
461 /*
462  * Search CHOICES[] for a table type matching NAME, return it.
463  * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
464  * several.
465  * CHOICES[] must be terminated with a negative value.
466  */
467 int
468 ef_byname_from(char *name, int choices[])
469 {
470     int res;
471     int *p;
472
473     res = M_NOTFOUND;
474     for (p = choices; *p >= 0; p++) {
475         if (ef_check(*p) < 0)
476             continue;
477         switch (mineq(name, empfile[*p].name)) {
478         case ME_MISMATCH:
479             break;
480         case ME_PARTIAL:
481             if (res >= 0)
482                 return M_NOTUNIQUE;
483             res = *p;
484             break;
485         case ME_EXACT:
486             return *p;
487         }
488     }
489     return res;
490 }
491
492 char *
493 ef_nameof(int type)
494 {
495     if (ef_check(type) < 0)
496         return "bad ef_type";
497     return empfile[type].name;
498 }
499
500 int
501 ef_check(int type)
502 {
503     if (CANT_HAPPEN((unsigned)type >= EF_MAX))
504         return -1;
505     return 0;
506 }
507
508 /*
509  * Ensure file-backed table contains ID.
510  * If necessary, extend it in steps of COUNT elements.
511  * Return non-zero on success, zero on failure.
512  */
513 int
514 ef_ensure_space(int type, int id, int count)
515 {
516     if (ef_check(type) < 0)
517         return 0;
518     CANT_HAPPEN(id < 0);
519
520     while (id >= empfile[type].fids) {
521         if (!ef_extend(type, count))
522             return 0;
523     }
524     return 1;
525 }