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