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