]> git.pond.sub.org Git - empserver/blob - src/lib/common/file.c
Fix type of get_seqno() from int to unsigned
[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-2008
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 ef_realloc_cache(struct empfile *, int);
50 static int fillcache(struct empfile *, int);
51 static int do_read(struct empfile *, void *, int, int);
52 static int do_write(struct empfile *, void *, int, int, time_t);
53 static unsigned get_seqno(struct empfile *, int);
54 static void new_seqno(struct empfile *, void *);
55 static void do_blank(struct empfile *, void *, int, int);
56
57 /*
58  * Open the file-backed table TYPE (EF_SECTOR, ...).
59  * HOW are flags to control operation.  Naturally, immutable flags are
60  * not permitted.
61  * Return non-zero on success, zero on failure.
62  * You must call ef_close() before the next ef_open().
63  */
64 int
65 ef_open(int type, int how)
66 {
67     struct empfile *ep;
68     struct flock lock;
69     int oflags, fd, fsiz, nslots;
70
71     if (ef_check(type) < 0)
72         return 0;
73     if (CANT_HAPPEN(how & EFF_IMMUTABLE))
74         how &= ~EFF_IMMUTABLE;
75
76     /* open file */
77     ep = &empfile[type];
78     if (CANT_HAPPEN(ep->fd >= 0))
79         return 0;
80     oflags = O_RDWR;
81     if (how & EFF_PRIVATE)
82         oflags = O_RDONLY;
83     if (how & EFF_CREATE)
84         oflags |= O_CREAT | O_TRUNC;
85 #if defined(_WIN32)
86     oflags |= O_BINARY;
87 #endif
88     if ((fd = open(ep->file, oflags, S_IRWUG)) < 0) {
89         logerror("Can't open %s (%s)", ep->file, strerror(errno));
90         return 0;
91     }
92
93     lock.l_type = how & EFF_PRIVATE ? F_RDLCK : F_WRLCK;
94     lock.l_whence = SEEK_SET;
95     lock.l_start = lock.l_len = 0;
96     if (fcntl(fd, F_SETLK, &lock) == -1) {
97         logerror("Can't lock %s (%s)", ep->file, strerror(errno));
98         close(fd);
99         return 0;
100     }
101
102     /* get file size */
103     fsiz = fsize(fd);
104     if (fsiz % ep->size) {
105         logerror("Can't open %s (file size not a multiple of record size %d)",
106                  ep->file, ep->size);
107         close(fd);
108         return 0;
109     }
110     ep->fids = fsiz / ep->size;
111
112     /* allocate cache */
113     if (ep->flags & EFF_STATIC) {
114         /* ep->cache already points to space for ep->csize elements */
115         if (how & EFF_MEM) {
116             if (ep->fids > ep->csize) {
117                 logerror("Can't open %s: file larger than %d bytes",
118                          ep->file, ep->fids * ep->size);
119                 close(fd);
120                 return 0;
121             }
122         }
123     } else {
124         if (CANT_HAPPEN(ep->cache))
125             free(ep->cache);
126         if (how & EFF_MEM)
127             nslots = ep->fids;
128         else
129             nslots = blksize(fd) / ep->size;
130         if (!ef_realloc_cache(ep, nslots)) {
131             logerror("Can't map %s (%s)", ep->file, strerror(errno));
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  * Reallocate cache for table EP to hold COUNT slots.
156  * The table must not be allocated statically.
157  * The cache may still be unmapped.
158  * If reallocation succeeds, any pointers obtained from ef_ptr()
159  * become invalid.
160  * If it fails, the cache is unchanged, and errno is set.
161  * Return non-zero on success, zero on failure.
162  */
163 static int
164 ef_realloc_cache(struct empfile *ep, int count)
165 {
166     void *cache;
167
168     if (CANT_HAPPEN(ep->flags & EFF_STATIC))
169         return 0;
170     if (CANT_HAPPEN(count < 0))
171         count = 0;
172
173     /*
174      * Avoid zero slots, because that can lead to null cache, which
175      * would be interpreted as unmapped cache.
176      */
177     if (count == 0)
178         count++;
179     cache = realloc(ep->cache, count * ep->size);
180     if (!cache)
181         return 0;
182
183     ep->cache = cache;
184     ep->csize = count;
185     return 1;
186 }
187
188 /*
189  * Close the file-backed table TYPE (EF_SECTOR, ...).
190  * Return non-zero on success, zero on failure.
191  */
192 int
193 ef_close(int type)
194 {
195     struct empfile *ep;
196     int retval;
197
198     retval = ef_flush(type);
199     ep = &empfile[type];
200     ep->flags &= EFF_IMMUTABLE;
201     if (!(ep->flags & EFF_STATIC)) {
202         free(ep->cache);
203         ep->cache = NULL;
204     }
205     if (close(ep->fd) < 0) {
206         logerror("Error closing %s (%s)", ep->name, strerror(errno));
207         retval = 0;
208     }
209     ep->fd = -1;
210     return retval;
211 }
212
213 /*
214  * Flush table TYPE (EF_SECTOR, ...) to disk.
215  * Does nothing if the table is privately mapped.
216  * Return non-zero on success, zero on failure.
217  */
218 int
219 ef_flush(int type)
220 {
221     struct empfile *ep;
222
223     if (ef_check(type) < 0)
224         return 0;
225     ep = &empfile[type];
226     if (ep->flags & EFF_PRIVATE)
227         return 1;               /* nothing to do */
228     if (CANT_HAPPEN(ep->fd < 0))
229         return 0;
230     /*
231      * We don't know which cache entries are dirty.  ef_write() writes
232      * through, but direct updates through ef_ptr() don't.  They are
233      * allowed only with EFF_MEM.  Assume the whole cash is dirty
234      * then.
235      */
236     if (ep->flags & EFF_MEM) {
237         if (do_write(ep, ep->cache, ep->baseid, ep->cids, time(NULL)) < 0)
238             return 0;
239     }
240
241     return 1;
242 }
243
244 /*
245  * Return pointer to element ID in table TYPE if it exists, else NULL.
246  * The table must be fully cached, i.e. flags & EFF_MEM.
247  * The caller is responsible for flushing changes he makes.
248  */
249 void *
250 ef_ptr(int type, int id)
251 {
252     struct empfile *ep;
253
254     if (ef_check(type) < 0)
255         return NULL;
256     ep = &empfile[type];
257     if (CANT_HAPPEN(!(ep->flags & EFF_MEM) || !ep->cache))
258         return NULL;
259     if (id < 0 || id >= ep->fids)
260         return NULL;
261     return ep->cache + ep->size * id;
262 }
263
264 /*
265  * Read element ID from table TYPE into buffer INTO.
266  * FIXME pass buffer size!
267  * Return non-zero on success, zero on failure.
268  */
269 int
270 ef_read(int type, int id, void *into)
271 {
272     struct empfile *ep;
273     void *from;
274
275     if (ef_check(type) < 0)
276         return 0;
277     ep = &empfile[type];
278     if (CANT_HAPPEN(!ep->cache))
279         return 0;
280     if (id < 0 || id >= ep->fids)
281         return 0;
282
283     if (ep->flags & EFF_MEM) {
284         from = ep->cache + id * ep->size;
285     } else {
286         if (ep->baseid + ep->cids <= id || ep->baseid > id) {
287             if (fillcache(ep, id) < 1)
288                 return 0;
289         }
290         from = ep->cache + (id - ep->baseid) * ep->size;
291     }
292     memcpy(into, from, ep->size);
293
294     if (ep->postread)
295         ep->postread(id, into);
296     return 1;
297 }
298
299 /*
300  * Fill cache of file-backed EP with elements starting at ID.
301  * If any were read, return their number.
302  * Else return -1 and leave the cache unchanged.
303  */
304 static int
305 fillcache(struct empfile *ep, int id)
306 {
307     int ret;
308
309     if (CANT_HAPPEN(!ep->cache))
310         return -1;
311
312     ret = do_read(ep, ep->cache, id, MIN(ep->csize, ep->fids - id));
313     if (ret >= 0) {
314         /* cache changed */
315         ep->baseid = id;
316         ep->cids = ret;
317     }
318     return ret;
319 }
320
321 static int
322 do_read(struct empfile *ep, void *buf, int id, int count)
323 {
324     int n, ret;
325     char *p;
326
327     if (CANT_HAPPEN(ep->fd < 0 || id < 0 || count < 0))
328         return -1;
329
330     if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) {
331         logerror("Error seeking %s to elt %d (%s)",
332                  ep->file, id, strerror(errno));
333         return -1;
334     }
335
336     p = buf;
337     n = count * ep->size;
338     while (n > 0) {
339         ret = read(ep->fd, p, n);
340         if (ret < 0) {
341             if (errno != EINTR) {
342                 logerror("Error reading %s elt %d (%s)",
343                          ep->file,
344                          id + (int)((p - (char *)buf) / ep->size),
345                          strerror(errno));
346                 break;
347             }
348         } else if (ret == 0) {
349             logerror("Unexpected EOF reading %s elt %d",
350                      ep->file, id + (int)((p - (char *)buf) / ep->size));
351             break;
352         } else {
353             p += ret;
354             n -= ret;
355         }
356     }
357
358     return (p - (char *)buf) / ep->size;
359 }
360
361 /*
362  * Write COUNT elements starting at ID from BUF to file-backed EP.
363  * Set the timestamp to NOW if the table has those.
364  * Return 0 on success, -1 on error (file may be corrupt then).
365  */
366 static int
367 do_write(struct empfile *ep, void *buf, int id, int count, time_t now)
368 {
369     int i, n, ret;
370     char *p;
371     struct emptypedstr *elt;
372
373     if (CANT_HAPPEN(ep->fd < 0 || (ep->flags & EFF_PRIVATE)
374                     || id < 0 || count < 0))
375         return -1;
376
377     if (ep->flags & EFF_TYPED) {
378         for (i = 0; i < count; i++) {
379             /*
380              * TODO Oopses here could be due to bad data corruption.
381              * Fail instead of attempting to recover?
382              */
383             elt = (struct emptypedstr *)((char *)buf + i * ep->size);
384             if (CANT_HAPPEN(elt->ef_type != ep->uid))
385                 elt->ef_type = ep->uid;
386             if (CANT_HAPPEN(elt->uid != id + i))
387                 elt->uid = id + i;
388             elt->timestamp = now;
389         }
390     }
391
392     if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) {
393         logerror("Error seeking %s to elt %d (%s)",
394                  ep->file, id, strerror(errno));
395         return -1;
396     }
397
398     p = buf;
399     n = count * ep->size;
400     while (n > 0) {
401         ret = write(ep->fd, p, n);
402         if (ret < 0) {
403             if (errno != EINTR) {
404                 logerror("Error writing %s elt %d (%s)",
405                          ep->file,
406                          id + (int)((p - (char *)buf) / ep->size),
407                          strerror(errno));
408                 return -1;
409             }
410         } else {
411             p += ret;
412             n -= ret;
413         }
414     }
415
416     return 0;
417 }
418
419 /*
420  * Write element ID into table TYPE from buffer FROM.
421  * FIXME pass buffer size!
422  * If table is file-backed and not privately mapped, write through
423  * cache straight to disk.
424  * Cannot write beyond the end of fully cached table (flags & EFF_MEM).
425  * Can write at the end of partially cached table.
426  * Return non-zero on success, zero on failure.
427  */
428 int
429 ef_write(int type, int id, void *from)
430 {
431     struct empfile *ep;
432     char *to;
433
434     if (ef_check(type) < 0)
435         return 0;
436     ep = &empfile[type];
437     if (CANT_HAPPEN((ep->flags & (EFF_MEM | EFF_PRIVATE)) == EFF_PRIVATE))
438         return 0;
439     if (ep->prewrite)
440         ep->prewrite(id, from);
441     if (CANT_HAPPEN((ep->flags & EFF_MEM) ? id >= ep->fids : id > ep->fids))
442         return 0;               /* not implemented */
443     new_seqno(ep, from);
444     if (!(ep->flags & EFF_PRIVATE)) {
445         if (do_write(ep, from, id, 1, time(NULL)) < 0)
446             return 0;
447     }
448     if (id >= ep->baseid && id < ep->baseid + ep->cids) {
449         /* update the cache if necessary */
450         to = ep->cache + (id - ep->baseid) * ep->size;
451         if (to != from)
452             memcpy(to, from, ep->size);
453     }
454     if (id >= ep->fids) {
455         /* write beyond end of file extends it, take note */
456         ep->fids = id + 1;
457     }
458     return 1;
459 }
460
461 /*
462  * Change element id.
463  * BUF is an element of table TYPE.
464  * ID is its new element ID.
465  * If table is EFF_TYPED, change id and sequence number stored in BUF.
466  * Else do nothing.
467  */
468 void
469 ef_set_uid(int type, void *buf, int uid)
470 {
471     struct emptypedstr *elt;
472     struct empfile *ep;
473
474     if (ef_check(type) < 0)
475         return;
476     ep = &empfile[type];
477     if (!(ep->flags & EFF_TYPED))
478         return;
479     elt = buf;
480     if (elt->uid == uid)
481         return;
482     elt->uid = uid;
483     elt->seqno = get_seqno(ep, uid);
484 }
485
486 /*
487  * Return sequence number of element ID in table EP.
488  * Return zero if table is not EFF_TYPED (it has no sequence number
489  * then).
490  */
491 static unsigned
492 get_seqno(struct empfile *ep, int id)
493 {
494     struct emptypedstr *elt;
495
496     if (!(ep->flags & EFF_TYPED))
497         return 0;
498     if (id < 0 || id >= ep->fids)
499         return 0;
500     if (id >= ep->baseid && id < ep->baseid + ep->cids)
501         elt = (void *)(ep->cache + (id - ep->baseid) * ep->size);
502     else {
503         /* need a buffer, steal last cache slot */
504         if (ep->cids == ep->csize)
505             ep->cids--;
506         elt = (void *)(ep->cache + ep->cids * ep->size);
507         if (do_read(ep, elt, id, 1) < 0)
508             return 0;           /* deep trouble */
509     }
510     return elt->seqno;
511 }
512
513 /*
514  * Increment sequence number in BUF, which is about to be written to EP.
515  * Do nothing if table is not EFF_TYPED (it has no sequence number
516  * then).
517  */
518 static void
519 new_seqno(struct empfile *ep, void *buf)
520 {
521     struct emptypedstr *elt = buf;
522     unsigned old_seqno;
523
524     if (!(ep->flags & EFF_TYPED))
525         return;
526     old_seqno = get_seqno(ep, elt->uid);
527     if (CANT_HAPPEN(old_seqno != elt->seqno))
528         old_seqno = MAX(old_seqno, elt->seqno);
529     elt->seqno = old_seqno + 1;
530 }
531
532 /*
533  * Extend table TYPE by COUNT elements.
534  * Any pointers obtained from ef_ptr() become invalid.
535  * Return non-zero on success, zero on failure.
536  */
537 int
538 ef_extend(int type, int count)
539 {
540     struct empfile *ep;
541     char *p;
542     int i, id;
543     time_t now = time(NULL);
544
545     if (ef_check(type) < 0)
546         return 0;
547     ep = &empfile[type];
548     if (CANT_HAPPEN(count < 0))
549         return 0;
550
551     id = ep->fids;
552     if (ep->flags & EFF_MEM) {
553         if (id + count > ep->csize) {
554             if (ep->flags & EFF_STATIC) {
555                 logerror("Can't extend %s beyond %d elements",
556                          ep->file, ep->csize);
557                 return 0;
558             }
559             if (!ef_realloc_cache(ep, id + count)) {
560                 logerror("Can't extend %s to %d elements (%s)",
561                          ep->file, id + count, strerror(errno));
562                 return 0;
563             }
564         }
565         p = ep->cache + id * ep->size;
566         do_blank(ep, p, id, count);
567         if (ep->fd >= 0 && !(ep->flags & EFF_PRIVATE)) {
568             if (do_write(ep, p, id, count, now) < 0)
569                 return 0;
570         }
571         ep->cids += count;
572     } else {
573         /* need a buffer, steal last cache slot */
574         if (ep->cids == ep->csize)
575             ep->cids--;
576         p = ep->cache + ep->cids * ep->size;
577         for (i = 0; i < count; i++) {
578             do_blank(ep, p, id + i, 1);
579             if (do_write(ep, p, id + i, 1, now) < 0)
580                 return 0;
581         }
582     }
583     ep->fids += count;
584     return 1;
585 }
586
587 /*
588  * Initialize element ID for EP in BUF.
589  * FIXME pass buffer size!
590  */
591 void
592 ef_blank(int type, int id, void *buf)
593 {
594     struct empfile *ep;
595     struct emptypedstr *elt;
596
597     if (ef_check(type) < 0)
598         return;
599     ep = &empfile[type];
600     do_blank(ep, buf, id, 1);
601     if (ep->flags & EFF_TYPED) {
602         elt = buf;
603         elt->seqno = get_seqno(ep, elt->uid);
604     }
605 }
606
607 /*
608  * Initialize COUNT elements of EP in BUF, starting with element ID.
609  */
610 static void
611 do_blank(struct empfile *ep, void *buf, int id, int count)
612 {
613     int i;
614     struct emptypedstr *elt;
615
616     memset(buf, 0, count * ep->size);
617     if (ep->flags & EFF_TYPED) {
618         for (i = 0; i < count; i++) {
619             elt = (struct emptypedstr *)((char *)buf + i * ep->size);
620             elt->ef_type = ep->uid;
621             elt->uid = id + i;
622         }
623     }
624 }
625
626 /*
627  * Truncate table TYPE to COUNT elements.
628  * Any pointers obtained from ef_ptr() become invalid.
629  * Return non-zero on success, zero on failure.
630  */
631 int
632 ef_truncate(int type, int count)
633 {
634     struct empfile *ep;
635
636     if (ef_check(type) < 0)
637         return 0;
638     ep = &empfile[type];
639     if (CANT_HAPPEN(count < 0 || count > ep->fids))
640         return 0;
641
642     if (ep->fd >= 0 && !(ep->flags & EFF_PRIVATE)) {
643         if (ftruncate(ep->fd, count * ep->size) < 0) {
644             logerror("Can't truncate %s to %d elements (%s)",
645                      ep->file, count, strerror(errno));
646             return 0;
647         }
648     }
649     ep->fids = count;
650
651     if (ep->flags & EFF_MEM) {
652         if (!(ep->flags & EFF_STATIC)) {
653             if (!ef_realloc_cache(ep, count)) {
654                 logerror("Can't shrink cache after truncate");
655                 /* continue with unshrunk cache */
656             }
657         }
658         ep->cids = count;
659     } else {
660         if (ep->baseid >= count)
661             ep->cids = 0;
662         else if (ep->cids > count - ep->baseid)
663             ep->cids = count - ep->baseid;
664     }
665
666     return 1;
667 }
668
669 struct castr *
670 ef_cadef(int type)
671 {
672     return empfile[type].cadef;
673 }
674
675 int
676 ef_nelem(int type)
677 {
678     return empfile[type].fids;
679 }
680
681 int
682 ef_flags(int type)
683 {
684     return empfile[type].flags;
685 }
686
687 time_t
688 ef_mtime(int type)
689 {
690     if (empfile[type].fd <= 0)
691         return 0;
692     return fdate(empfile[type].fd);
693 }
694
695 /*
696  * Search for a table matching NAME, return its table type.
697  * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
698  * several.
699  */
700 int
701 ef_byname(char *name)
702 {
703     return stmtch(name, empfile, offsetof(struct empfile, name),
704                   sizeof(empfile[0]));
705 }
706
707 /*
708  * Search CHOICES[] for a table type matching NAME, return it.
709  * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
710  * several.
711  * CHOICES[] must be terminated with a negative value.
712  */
713 int
714 ef_byname_from(char *name, int choices[])
715 {
716     int res;
717     int *p;
718
719     res = M_NOTFOUND;
720     for (p = choices; *p >= 0; p++) {
721         if (ef_check(*p) < 0)
722             continue;
723         switch (mineq(name, empfile[*p].name)) {
724         case ME_MISMATCH:
725             break;
726         case ME_PARTIAL:
727             if (res >= 0)
728                 return M_NOTUNIQUE;
729             res = *p;
730             break;
731         case ME_EXACT:
732             return *p;
733         }
734     }
735     return res;
736 }
737
738 char *
739 ef_nameof(int type)
740 {
741     if (ef_check(type) < 0)
742         return "bad ef_type";
743     return empfile[type].name;
744 }
745
746 int
747 ef_check(int type)
748 {
749     if (CANT_HAPPEN((unsigned)type >= EF_MAX))
750         return -1;
751     return 0;
752 }
753
754 /*
755  * Ensure file-backed table contains ID.
756  * If necessary, extend it in steps of COUNT elements.
757  * Return non-zero on success, zero on failure.
758  */
759 int
760 ef_ensure_space(int type, int id, int count)
761 {
762     if (ef_check(type) < 0)
763         return 0;
764     CANT_HAPPEN(id < 0);
765
766     while (id >= empfile[type].fids) {
767         if (!ef_extend(type, count))
768             return 0;
769     }
770     return 1;
771 }