]> git.pond.sub.org Git - empserver/blob - src/lib/common/file.c
ebf5ab259c8aec45c196fe17c083ce5f02c89dc8
[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     return 1;
152 }
153
154 /*
155  * Close the file-backed table TYPE (EF_SECTOR, ...).
156  * Return non-zero on success, zero on failure.
157  */
158 int
159 ef_close(int type)
160 {
161     struct empfile *ep;
162     int retval;
163
164     retval = ef_flush(type);
165     ep = &empfile[type];
166     ep->flags &= EFF_IMMUTABLE;
167     if (!(ep->flags & EFF_STATIC)) {
168         free(ep->cache);
169         ep->cache = NULL;
170     }
171     if (close(ep->fd) < 0) {
172         logerror("Error closing %s (%s)", ep->name, strerror(errno));
173         retval = 0;
174     }
175     ep->fd = -1;
176     return retval;
177 }
178
179 /*
180  * Flush file-backed table TYPE (EF_SECTOR, ...) to disk.
181  * Return non-zero on success, zero on failure.
182  */
183 int
184 ef_flush(int type)
185 {
186     struct empfile *ep;
187
188     if (ef_check(type) < 0)
189         return 0;
190     ep = &empfile[type];
191     if (CANT_HAPPEN(ep->fd < 0))
192         return 0;
193     /*
194      * We don't know which cache entries are dirty.  ef_write() writes
195      * through, but direct updates through ef_ptr() don't.  They are
196      * allowed only with EFF_MEM.  Assume the whole cash is dirty
197      * then.
198      */
199     if (!(ep->flags & EFF_RDONLY) && (ep->flags & EFF_MEM))
200         return do_write(ep, ep->cache, ep->baseid, ep->cids) >= 0;
201
202     return 1;
203 }
204
205 /*
206  * Return pointer to element ID in table TYPE if it exists, else NULL.
207  * The table must be fully cached, i.e. flags & EFF_MEM.
208  * The caller is responsible for flushing changes he makes.
209  */
210 void *
211 ef_ptr(int type, int id)
212 {
213     struct empfile *ep;
214
215     if (ef_check(type) < 0)
216         return NULL;
217     ep = &empfile[type];
218     if (CANT_HAPPEN(!(ep->flags & EFF_MEM) || !ep->cache))
219         return NULL;
220     if (id < 0 || id >= ep->fids)
221         return NULL;
222     return ep->cache + ep->size * id;
223 }
224
225 /*
226  * Read element ID from table TYPE into buffer INTO.
227  * FIXME pass buffer size!
228  * Return non-zero on success, zero on failure.
229  */
230 int
231 ef_read(int type, int id, void *into)
232 {
233     struct empfile *ep;
234     void *from;
235
236     if (ef_check(type) < 0)
237         return 0;
238     ep = &empfile[type];
239     if (CANT_HAPPEN(!ep->cache))
240         return 0;
241     if (id < 0 || id >= ep->fids)
242         return 0;
243
244     if (ep->flags & EFF_MEM) {
245         from = ep->cache + id * ep->size;
246     } else {
247         if (ep->baseid + ep->cids <= id || ep->baseid > id) {
248             if (fillcache(ep, id) < 1)
249                 return 0;
250         }
251         from = ep->cache + (id - ep->baseid) * ep->size;
252     }
253     memcpy(into, from, ep->size);
254
255     if (ep->postread)
256         ep->postread(id, into);
257     return 1;
258 }
259
260 /*
261  * Fill cache of EP with elements starting at ID.
262  * If any were read, return their number.
263  * Else return -1 and leave the cache unchanged.
264  */
265 static int
266 fillcache(struct empfile *ep, int start)
267 {
268     int n, ret;
269     char *p;
270
271     if (CANT_HAPPEN(ep->fd < 0 || !ep->cache))
272         return -1;
273
274     if (lseek(ep->fd, start * ep->size, SEEK_SET) == (off_t)-1) {
275         logerror("Error seeking %s (%s)", ep->file, strerror(errno));
276         return -1;
277     }
278
279     p = ep->cache;
280     n = ep->csize * ep->size;
281     while (n > 0) {
282         ret = read(ep->fd, p, n);
283         if (ret < 0) {
284             if (errno != EAGAIN) {
285                 logerror("Error reading %s (%s)", ep->file, strerror(errno));
286                 break;
287             }
288         } else if (ret == 0) {
289             break;
290         } else {
291             p += ret;
292             n -= ret;
293         }
294     }
295
296     if (p == ep->cache)
297         return -1;              /* nothing read, old cache still ok */
298
299     ep->baseid = start;
300     ep->cids = (p - ep->cache) / ep->size;
301     return ep->cids;
302 }
303
304 /*
305  * Write COUNT elements from BUF to EP, starting at ID.
306  * Return 0 on success, -1 on error.
307  */
308 static int
309 do_write(struct empfile *ep, void *buf, int id, int count)
310 {
311     int n, ret;
312     char *p;
313
314     if (CANT_HAPPEN(ep->fd < 0 || id < 0 || count < 0))
315         return -1;
316
317     if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) {
318         logerror("Error seeking %s (%s)", ep->file, strerror(errno));
319         return -1;
320     }
321
322     p = buf;
323     n = count * ep->size;
324     while (n > 0) {
325         ret = write(ep->fd, p, n);
326         if (ret < 0) {
327             if (errno != EAGAIN) {
328                 logerror("Error writing %s (%s)", ep->file, strerror(errno));
329                 /* FIXME if this extended file, truncate back to old size */
330                 return -1;
331             }
332         } else {
333             p += ret;
334             n -= ret;
335         }
336     }
337
338     return 0;
339 }
340
341 /*
342  * Write element ID into file-backed table TYPE from buffer FROM.
343  * FIXME pass buffer size!
344  * Write through cache straight to disk.
345  * Cannot write beyond the end of fully cached table (flags & EFF_MEM).
346  * Can write at the end of partially cached table.
347  * Return non-zero on success, zero on failure.
348  */
349 int
350 ef_write(int type, int id, void *from)
351 {
352     struct empfile *ep;
353     char *to;
354
355     if (ef_check(type) < 0)
356         return 0;
357     ep = &empfile[type];
358     if (ep->prewrite)
359         ep->prewrite(id, from);
360     if (CANT_HAPPEN((ep->flags & EFF_MEM) ? id >= ep->fids : id > ep->fids))
361         return 0;               /* not implemented */
362     if (do_write(ep, from, id, 1) < 0)
363         return 0;
364     if (id >= ep->baseid && id < ep->baseid + ep->cids) {
365         /* update the cache if necessary */
366         to = ep->cache + (id - ep->baseid) * ep->size;
367         if (to != from)
368             memcpy(to, from, ep->size);
369     }
370     if (id >= ep->fids) {
371         /* write beyond end of file extends it, take note */
372         ep->fids = id + 1;
373     }
374     return 1;
375 }
376
377 /*
378  * Extend the file-backed table TYPE by COUNT elements.
379  * Return non-zero on success, zero on failure.
380  */
381 int
382 ef_extend(int type, int count)
383 {
384     struct empfile *ep;
385     void *tmpobj;
386     int id, i, how;
387
388     if (ef_check(type) < 0)
389         return 0;
390     ep = &empfile[type];
391     if (CANT_HAPPEN(ep->fd < 0 || count < 0))
392         return 0;
393
394     tmpobj = calloc(1, ep->size);
395     id = ep->fids;
396     for (i = 0; i < count; i++) {
397         if (ep->init)
398             ep->init(id + i, tmpobj);
399         if (do_write(ep, tmpobj, id + i, 1) < 0)
400             break;
401     }
402     free(tmpobj);
403
404     if (ep->flags & EFF_MEM) {
405         /* FIXME lazy bastards...  do this right */
406         /* XXX this will cause problems if there are ef_ptrs (to the
407          * old allocated structure) active when we do the re-open */
408         how = ep->flags & ~EFF_IMMUTABLE;
409         ef_close(type);
410         ef_open(type, how);
411     } else {
412         ep->fids += i;
413     }
414
415     return i == count;
416 }
417
418 struct castr *
419 ef_cadef(int type)
420 {
421     return empfile[type].cadef;
422 }
423
424 int
425 ef_nelem(int type)
426 {
427     return empfile[type].fids;
428 }
429
430 int
431 ef_flags(int type)
432 {
433     return empfile[type].flags;
434 }
435
436 time_t
437 ef_mtime(int type)
438 {
439     if (empfile[type].fd <= 0)
440         return 0;
441     return fdate(empfile[type].fd);
442 }
443
444 /*
445  * Search for a table matching NAME, return its table type.
446  * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
447  * several.
448  */
449 int
450 ef_byname(char *name)
451 {
452     return stmtch(name, empfile, offsetof(struct empfile, name),
453                   sizeof(empfile[0]));
454 }
455
456 /*
457  * Search CHOICES[] for a table type matching NAME, return it.
458  * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
459  * several.
460  * CHOICES[] must be terminated with a negative value.
461  */
462 int
463 ef_byname_from(char *name, int choices[])
464 {
465     int res;
466     int *p;
467
468     res = M_NOTFOUND;
469     for (p = choices; *p >= 0; p++) {
470         if (ef_check(*p) < 0)
471             continue;
472         switch (mineq(name, empfile[*p].name)) {
473         case ME_MISMATCH:
474             break;
475         case ME_PARTIAL:
476             if (res >= 0)
477                 return M_NOTUNIQUE;
478             res = *p;
479             break;
480         case ME_EXACT:
481             return *p;
482         }
483     }
484     return res;
485 }
486
487 char *
488 ef_nameof(int type)
489 {
490     if (ef_check(type) < 0)
491         return "bad ef_type";
492     return empfile[type].name;
493 }
494
495 int
496 ef_check(int type)
497 {
498     if (CANT_HAPPEN((unsigned)type >= EF_MAX))
499         return -1;
500     return 0;
501 }
502
503 /*
504  * Ensure file-backed table contains ID.
505  * If necessary, extend it in steps of COUNT elements.
506  * Return non-zero on success, zero on failure.
507  */
508 int
509 ef_ensure_space(int type, int id, int count)
510 {
511     if (ef_check(type) < 0)
512         return 0;
513     CANT_HAPPEN(id < 0);
514
515     while (id >= empfile[type].fids) {
516         if (!ef_extend(type, count))
517             return 0;
518     }
519     return 1;
520 }