]> git.pond.sub.org Git - empserver/blob - src/lib/common/file.c
(ef_ptr, ef_read): Oops on bad ID. Some callers rely on these
[empserver] / src / lib / common / file.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2005, 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 the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  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
34  */
35
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #if !defined(_WIN32)
42 #include <unistd.h>
43 #endif
44 #include "common.h"
45 #include "file.h"
46 #include "gen.h"
47 #include "match.h"
48 #include "misc.h"
49 #include "nsc.h"
50 #include "optlist.h"
51
52 static int fillcache(struct empfile *, int);
53 static int do_write(struct empfile *, void *, int, int);
54
55 /*
56  * Open the file-backed table TYPE (EF_SECTOR, ...).
57  * HOW are flags to control operation.  Naturally, immutable flags are
58  * not permitted.
59  * Return non-zero on success, zero on failure.
60  * You must call ef_close() before the next ef_open().
61  */
62 int
63 ef_open(int type, int how)
64 {
65     struct empfile *ep;
66     int oflags, fd, fsiz, size;
67
68     if (ef_check(type) < 0)
69         return 0;
70     if (CANT_HAPPEN(how & EFF_IMMUTABLE))
71         how &= ~EFF_IMMUTABLE;
72
73     /* open file */
74     ep = &empfile[type];
75     if (CANT_HAPPEN(ep->fd >= 0))
76         return 0;
77     oflags = O_RDWR;
78     if (how & EFF_RDONLY)
79         oflags = O_RDONLY;
80     if (how & EFF_CREATE)
81         oflags |= O_CREAT | O_TRUNC;
82 #if defined(_WIN32)
83     oflags |= O_BINARY;
84 #endif
85     if ((fd = open(ep->file, oflags, 0660)) < 0) {
86         logerror("Can't open %s (%s)", ep->file, strerror(errno));
87         return 0;
88     }
89
90     /* get file size */
91     fsiz = fsize(fd);
92     if (fsiz % ep->size) {
93         logerror("Can't open %s (file size not a multiple of record size %d)",
94                  ep->file, ep->size);
95         close(fd);
96         return 0;
97     }
98     ep->fids = fsiz / ep->size;
99
100     /* allocate cache */
101     if (ep->flags & EFF_STATIC) {
102         /* ep->cache already points to space for e->csize elements */
103         if (how & EFF_MEM) {
104             if (ep->fids > ep->csize) {
105                 logerror("Can't open %s: file larger than %d bytes",
106                          ep->file, ep->fids * ep->size);
107                 close(fd);
108                 return 0;
109             }
110         }
111     } else {
112         if (how & EFF_MEM)
113             ep->csize = ep->fids;
114         else
115             ep->csize = max(1, blksize(fd) / ep->size);
116         size = ep->csize * ep->size;
117         if (CANT_HAPPEN(ep->cache))
118             free(ep->cache);
119         ep->cache = malloc(size);
120         if (ep->cache == NULL && size) {
121             logerror("Can't open %s: out of memory", ep->file);
122             close(fd);
123             return 0;
124         }
125     }
126     ep->baseid = 0;
127     ep->cids = 0;
128     ep->flags = (ep->flags & EFF_IMMUTABLE) | (how & ~EFF_CREATE);
129     ep->fd = fd;
130
131     /* map file into cache */
132     if ((how & EFF_MEM) && ep->fids) {
133         if (fillcache(ep, 0) != ep->fids) {
134             ep->cids = 0;       /* prevent cache flush */
135             ep->flags &= EFF_IMMUTABLE; /* maintain invariant */
136             ef_close(type);
137             return 0;
138         }
139     }
140
141     /*
142      * Could close fd if both EFF_RDONLY and EFF_MEM, but that doesn't
143      * happen, so don't bother.
144      */
145
146     return 1;
147 }
148
149 /*
150  * Close the file-backed table TYPE (EF_SECTOR, ...).
151  * Return non-zero on success, zero on failure.
152  */
153 int
154 ef_close(int type)
155 {
156     struct empfile *ep;
157     int retval;
158
159     retval = ef_flush(type);
160     ep = &empfile[type];
161     ep->flags &= EFF_IMMUTABLE;
162     if (!(ep->flags & EFF_STATIC)) {
163         free(ep->cache);
164         ep->cache = NULL;
165     }
166     if (close(ep->fd) < 0) {
167         logerror("Error closing %s (%s)", ep->name, strerror(errno));
168         retval = 0;
169     }
170     ep->fd = -1;
171     return retval;
172 }
173
174 /*
175  * Flush file-backed table TYPE (EF_SECTOR, ...) to disk.
176  * Return non-zero on success, zero on failure.
177  */
178 int
179 ef_flush(int type)
180 {
181     struct empfile *ep;
182
183     if (ef_check(type) < 0)
184         return 0;
185     ep = &empfile[type];
186     if (CANT_HAPPEN(ep->fd < 0))
187         return 0;
188     /*
189      * We don't know which cache entries are dirty.  ef_write() writes
190      * through, but direct updates through ef_ptr() don't.  They are
191      * allowed only with EFF_MEM.  Assume the whole cash is dirty
192      * then.
193      */
194     if (!(ep->flags & EFF_RDONLY) && (ep->flags & EFF_MEM))
195         return do_write(ep, ep->cache, ep->baseid, ep->cids) >= 0;
196
197     return 1;
198 }
199
200 /*
201  * Return pointer to element ID in table TYPE if it exists, else NULL.
202  * The table must be fully cached, i.e. flags & EFF_MEM.
203  * The caller is responsible for flushing changes he makes.
204  */
205 void *
206 ef_ptr(int type, int id)
207 {
208     struct empfile *ep;
209
210     if (ef_check(type) < 0)
211         return NULL;
212     ep = &empfile[type];
213     if (CANT_HAPPEN(!(ep->flags & EFF_MEM) || !ep->cache))
214         return NULL;
215     if (id < 0 || id >= ep->fids) {
216         CANT_HAPPEN(id != ep->fids);
217         return NULL;
218     }
219     return ep->cache + ep->size * id;
220 }
221
222 /*
223  * Read element ID from table TYPE into buffer INTO.
224  * FIXME pass buffer size!
225  * Return non-zero on success, zero on failure.
226  */
227 int
228 ef_read(int type, int id, void *into)
229 {
230     struct empfile *ep;
231     void *from;
232
233     if (ef_check(type) < 0)
234         return 0;
235     ep = &empfile[type];
236     if (CANT_HAPPEN(!ep->cache))
237         return 0;
238     if (id < 0 || id >= ep->fids) {
239         CANT_HAPPEN(id != ep->fids);
240         return 0;
241     }
242
243     if (ep->flags & EFF_MEM) {
244         from = ep->cache + id * ep->size;
245     } else {
246         if (ep->baseid + ep->cids <= id || ep->baseid > id) {
247             if (fillcache(ep, id) < 1)
248                 return 0;
249         }
250         from = ep->cache + (id - ep->baseid) * ep->size;
251     }
252     memcpy(into, from, ep->size);
253
254     if (ep->postread)
255         ep->postread(id, into);
256     return 1;
257 }
258
259 /*
260  * Fill cache of EP with elements starting at ID.
261  * If any were read, return their number.
262  * Else return -1 and leave the cache unchanged.
263  */
264 static int
265 fillcache(struct empfile *ep, int start)
266 {
267     int n, ret;
268     char *p;
269
270     if (CANT_HAPPEN(ep->fd < 0 || !ep->cache))
271         return -1;
272
273     if (lseek(ep->fd, start * ep->size, SEEK_SET) == (off_t)-1) {
274         logerror("Error seeking %s (%s)", ep->file, strerror(errno));
275         return -1;
276     }
277
278     p = ep->cache;
279     n = ep->csize * ep->size;
280     while (n > 0) {
281         ret = read(ep->fd, p, n);
282         if (ret < 0) {
283             if (errno != EAGAIN) {
284                 logerror("Error reading %s (%s)", ep->file, strerror(errno));
285                 break;
286             }
287         } else if (ret == 0) {
288             break;
289         } else {
290             p += ret;
291             n -= ret;
292         }
293     }
294
295     if (p == ep->cache)
296         return -1;              /* nothing read, old cache still ok */
297
298     ep->baseid = start;
299     ep->cids = (p - ep->cache) / ep->size;
300     return ep->cids;
301 }
302
303 /*
304  * Write COUNT elements from BUF to EP, starting at ID.
305  * Return 0 on success, -1 on error.
306  */
307 static int
308 do_write(struct empfile *ep, void *buf, int id, int count)
309 {
310     int n, ret;
311     char *p;
312
313     if (CANT_HAPPEN(ep->fd < 0 || id < 0 || count < 0))
314         return -1;
315
316     if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) {
317         logerror("Error seeking %s (%s)", ep->file, strerror(errno));
318         return -1;
319     }
320
321     p = buf;
322     n = count * ep->size;
323     while (n > 0) {
324         ret = write(ep->fd, p, n);
325         if (ret < 0) {
326             if (errno != EAGAIN) {
327                 logerror("Error writing %s (%s)", ep->file, strerror(errno));
328                 /* FIXME if this extended file, truncate back to old size */
329                 return -1;
330             }
331         } else {
332             p += ret;
333             n -= ret;
334         }
335     }
336
337     return 0;
338 }
339
340 /*
341  * Write element ID into file-backed table TYPE from buffer FROM.
342  * FIXME pass buffer size!
343  * Write through cache straight to disk.
344  * Cannot write beyond the end of fully cached table (flags & EFF_MEM).
345  * Can write at the end of partially cached table.
346  * Return non-zero on success, zero on failure.
347  */
348 int
349 ef_write(int type, int id, void *from)
350 {
351     struct empfile *ep;
352     char *to;
353
354     if (ef_check(type) < 0)
355         return 0;
356     ep = &empfile[type];
357     if (ep->prewrite)
358         ep->prewrite(id, from);
359     if (CANT_HAPPEN((ep->flags & EFF_MEM) ? id >= ep->fids : id > ep->fids))
360         return 0;               /* not implemented */
361     if (do_write(ep, from, id, 1) < 0)
362         return 0;
363     if (id >= ep->baseid && id < ep->baseid + ep->cids) {
364         /* update the cache if necessary */
365         to = ep->cache + (id - ep->baseid) * ep->size;
366         memcpy(to, from, ep->size);
367     }
368     if (id >= ep->fids) {
369         /* write beyond end of file extends it, take note */
370         ep->fids = id + 1;
371     }
372     return 1;
373 }
374
375 /*
376  * Extend the file-backed table TYPE by COUNT elements.
377  * Return non-zero on success, zero on failure.
378  */
379 int
380 ef_extend(int type, int count)
381 {
382     struct empfile *ep;
383     char *tmpobj;
384     int id, i, how;
385
386     if (ef_check(type) < 0)
387         return 0;
388     ep = &empfile[type];
389     if (CANT_HAPPEN(ep->fd < 0 || count < 0))
390         return 0;
391
392     tmpobj = calloc(1, ep->size);
393     id = ep->fids;
394     for (i = 0; i < count; i++) {
395         if (ep->init)
396             ep->init(id + i, tmpobj);
397         if (do_write(ep, tmpobj, id + i, 1) < 0)
398             break;
399     }
400     free(tmpobj);
401
402     if (ep->flags & EFF_MEM) {
403         /* FIXME lazy bastards...  do this right */
404         /* XXX this will cause problems if there are ef_ptrs (to the
405          * old allocated structure) active when we do the re-open */
406         how = ep->flags & ~EFF_IMMUTABLE;
407         ef_close(type);
408         ef_open(type, how);
409     } else {
410         ep->fids += i;
411     }
412
413     return i == count;
414 }
415
416 struct castr *
417 ef_cadef(int type)
418 {
419     return empfile[type].cadef;
420 }
421
422 int
423 ef_nelem(int type)
424 {
425     return empfile[type].fids;
426 }
427
428 int
429 ef_flags(int type)
430 {
431     return empfile[type].flags;
432 }
433
434 time_t
435 ef_mtime(int type)
436 {
437     if (empfile[type].fd <= 0)
438         return 0;
439     return fdate(empfile[type].fd);
440 }
441
442 /*
443  * Search for a table matching NAME, return its table type.
444  * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
445  * several.
446  */
447 int
448 ef_byname(char *name)
449 {
450     return stmtch(name, empfile, offsetof(struct empfile, name),
451                   sizeof(empfile[0]));
452 }
453
454 /*
455  * Search CHOICES[] for a table type matching NAME, return it.
456  * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
457  * several.
458  * CHOICES[] must be terminated with a negative value.
459  */
460 int
461 ef_byname_from(char *name, int choices[])
462 {
463     int res;
464     int *p;
465
466     res = M_NOTFOUND;
467     for (p = choices; *p >= 0; p++) {
468         if (ef_check(*p) < 0)
469             continue;
470         switch (mineq(name, empfile[*p].name)) {
471         case ME_MISMATCH:
472             break;
473         case ME_PARTIAL:
474             if (res >= 0)
475                 return M_NOTUNIQUE;
476             res = *p;
477             break;
478         case ME_EXACT:
479             return *p;
480         }
481     }
482     return res;
483 }
484
485 char *
486 ef_nameof(int type)
487 {
488     if (ef_check(type) < 0)
489         return "bad ef_type";
490     return empfile[type].name;
491 }
492
493 int
494 ef_check(int type)
495 {
496     if (CANT_HAPPEN((unsigned)type >= EF_MAX))
497         return -1;
498     return 0;
499 }
500
501 /*
502  * Ensure file-backed table contains ID.
503  * If necessary, extend it in steps of COUNT elements.
504  * Return non-zero on success, zero on failure.
505  */
506 int
507 ef_ensure_space(int type, int id, int count)
508 {
509     if (ef_check(type) < 0)
510         return 0;
511     CANT_HAPPEN(id < 0);
512
513     while (id >= empfile[type].fids) {
514         if (!ef_extend(type, count))
515             return 0;
516     }
517     return 1;
518 }
519
520 static void
521 ef_fix_size(struct empfile *ep, int n)
522 {
523     ep->cids = ep->fids = n;
524     ep->csize = n + 1;
525 }
526
527 static void
528 ef_init_chr(int type, size_t size, ptrdiff_t name_offs)
529 {
530     struct empfile *ep = &empfile[type];
531     char *p;
532
533     for (p = ep->cache;
534          *((char **)(p + name_offs)) && **((char **)(p + name_offs));
535          p += size) ;
536     ep->cids = ep->fids = (p - ep->cache) / size;
537 }
538
539 /*
540  * Initialize Empire tables.
541  * Must be called once, before using anything else from this module.
542  */
543 void
544 ef_init(void)
545 {
546     struct castr *ca;
547     struct empfile *ep;
548     struct symbol *lup;
549     int i;
550
551     empfile[EF_MAP].size = empfile[EF_BMAP].size = (WORLD_X * WORLD_Y) / 2;
552
553     ef_init_chr(EF_SHIP_CHR,
554                 sizeof(struct mchrstr), offsetof(struct mchrstr, m_name));
555     ef_init_chr(EF_PLANE_CHR,
556                 sizeof(struct plchrstr), offsetof(struct plchrstr, pl_name));
557     ef_init_chr(EF_LAND_CHR,
558                 sizeof(struct lchrstr), offsetof(struct lchrstr, l_name));
559     ef_init_chr(EF_NUKE_CHR,
560                 sizeof(struct nchrstr), offsetof(struct nchrstr, n_name));
561
562     ca = (struct castr *)empfile[EF_META].cache;
563     for (i = 0; ca[i].ca_name; i++) ;
564     ef_fix_size(&empfile[EF_META], i);
565
566     for (ep = empfile; ep->uid >= 0; ep++) {
567         if (ep->cadef == symbol_ca) {
568             lup = (struct symbol *)ep->cache;
569             for (i = 0; lup[i].name; i++) ;
570             ef_fix_size(ep, i);
571         }
572     }
573 }