ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/modules/database/fileutil.c
Revision: 3389
Committed: Fri Apr 25 14:12:15 2014 UTC (11 years, 4 months ago) by michael
Content type: text/x-csrc
File size: 10762 byte(s)
Log Message:
- Imported ircservices-5.1.24

File Contents

# User Rev Content
1 michael 3389 /* Database file handling routines.
2     *
3     * IRC Services is copyright (c) 1996-2009 Andrew Church.
4     * E-mail: <achurch@achurch.org>
5     * Parts written by Andrew Kempe and others.
6     * This program is free but copyrighted software; see the file GPL.txt for
7     * details.
8     */
9    
10     #include "services.h"
11     #ifndef CONVERT_DB
12     # include "modules.h"
13     #endif
14     #include <fcntl.h>
15    
16     #include "fileutil.h"
17    
18     /*************************************************************************/
19     /*************************************************************************/
20    
21     /* Return the version number of the file. Return -1 if there is no version
22     * number or the number doesn't make sense (i.e. less than 1).
23     */
24    
25     int32 get_file_version(dbFILE *f)
26     {
27     FILE *fp = f->fp;
28     int version = fgetc(fp)<<24 | fgetc(fp)<<16 | fgetc(fp)<<8 | fgetc(fp);
29     if (ferror(fp)) {
30     #ifndef CONVERT_DB
31     module_log_perror("Error reading version number on %s", f->filename);
32     #endif
33     return -1;
34     } else if (feof(fp)) {
35     #ifndef CONVERT_DB
36     module_log("Error reading version number on %s: End of file detected",
37     f->filename);
38     #endif
39     return -1;
40     } else if (version < 1) {
41     #ifndef CONVERT_DB
42     module_log("Invalid version number (%d) on %s", version, f->filename);
43     #endif
44     return -1;
45     }
46     return version;
47     }
48    
49     /*************************************************************************/
50    
51     /* Write the version number to the file. Return 0 on success, -1 on
52     * failure.
53     */
54    
55     int write_file_version(dbFILE *f, int32 filever)
56     {
57     FILE *fp = f->fp;
58     if (
59     fputc(filever>>24 & 0xFF, fp) < 0 ||
60     fputc(filever>>16 & 0xFF, fp) < 0 ||
61     fputc(filever>> 8 & 0xFF, fp) < 0 ||
62     fputc(filever & 0xFF, fp) < 0
63     ) {
64     #ifndef CONVERT_DB
65     module_log_perror("Error writing version number on %s", f->filename);
66     #endif
67     return -1;
68     }
69     return 0;
70     }
71    
72     /*************************************************************************/
73     /*************************************************************************/
74    
75     /* Helper functions for open_db(). */
76    
77     static dbFILE *open_db_read(const char *filename)
78     {
79     dbFILE *f;
80     FILE *fp;
81    
82     f = smalloc(sizeof(*f));
83     *f->tempname = 0;
84     strbcpy(f->filename, filename);
85     f->mode = 'r';
86     fp = fopen(f->filename, "rb");
87     if (!fp) {
88     int errno_save = errno;
89     #ifndef CONVERT_DB
90     if (errno != ENOENT)
91     module_log_perror("Can't read database file %s", f->filename);
92     #endif
93     free(f);
94     errno = errno_save;
95     return NULL;
96     }
97     f->fp = fp;
98     return f;
99     }
100    
101     /************************************/
102    
103     static dbFILE *open_db_write(const char *filename, int32 filever)
104     {
105     dbFILE *f;
106     int fd;
107    
108     f = smalloc(sizeof(*f));
109     *f->tempname = 0;
110     strbcpy(f->filename, filename);
111     filename = f->filename;
112     f->mode = 'w';
113    
114     snprintf(f->tempname, sizeof(f->tempname), "%s.new", filename);
115     if (!*f->tempname || strcmp(f->tempname, filename) == 0) {
116     #ifndef CONVERT_DB
117     module_log("Opening database file %s for write: Filename too long",
118     filename);
119     #endif
120     free(f);
121     errno = ENAMETOOLONG;
122     return NULL;
123     }
124     remove(f->tempname);
125     /* Use open() to avoid people sneaking a new file in under us */
126     fd = open(f->tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
127     if (fd >= 0)
128     f->fp = fdopen(fd, "wb");
129     if (!f->fp || write_file_version(f, filever) < 0) {
130     int errno_save = errno;
131     #ifndef CONVERT_DB
132     static int walloped = 0;
133     if (!walloped) {
134     walloped++;
135     wallops(NULL, "Can't create temporary database file %s",
136     f->tempname);
137     }
138     errno = errno_save;
139     module_log_perror("Can't create temporary database file %s",
140     f->tempname);
141     #endif
142     if (f->fp)
143     fclose(f->fp);
144     remove(f->tempname);
145     errno = errno_save;
146     return NULL;
147     }
148     return f;
149     }
150    
151     /*************************************************************************/
152    
153     /* Open a database file for reading (*mode == 'r') or writing (*mode == 'w').
154     * Return the stream pointer, or NULL on error. When opening for write, the
155     * file actually opened is a temporary file, which will be renamed to the
156     * original file on close.
157     *
158     * `version' is only used when opening a file for writing, and indicates the
159     * version number to write to the file.
160     */
161    
162     dbFILE *open_db(const char *filename, const char *mode, int32 version)
163     {
164     if (*mode == 'r') {
165     return open_db_read(filename);
166     } else if (*mode == 'w') {
167     return open_db_write(filename, version);
168     } else {
169     errno = EINVAL;
170     return NULL;
171     }
172     }
173    
174     /*************************************************************************/
175    
176     /* Close a database file. If the file was opened for write, moves the new
177     * file over the old one, and logs/wallops an error message if the rename()
178     * fails.
179     */
180    
181     int close_db(dbFILE *f)
182     {
183     int res;
184     if (!f->fp) {
185     errno = EINVAL;
186     return -1;
187     }
188     res = fclose(f->fp);
189     f->fp = NULL;
190     if (res != 0)
191     return -1;
192     if (f->mode=='w' && *f->tempname && strcmp(f->tempname,f->filename)!=0) {
193     if (rename(f->tempname, f->filename) < 0) {
194     #ifndef CONVERT_DB
195     int errno_save = errno;
196     wallops(NULL, "Unable to move new data to database file %s;"
197     " new data NOT saved.", f->filename);
198     errno = errno_save;
199     module_log_perror("Unable to move new data to database file %s;"
200     " new data NOT saved.", f->filename);
201     #endif
202     remove(f->tempname);
203     }
204     }
205     free(f);
206     return 0;
207     }
208    
209     /*************************************************************************/
210    
211     /* Restore the database file to its condition before open_db(). This is
212     * identical to close_db() for files open for reading; however, for files
213     * open for writing, we discard the new temporary file instead of renaming
214     * it over the old file. The value of errno is preserved.
215     */
216    
217     void restore_db(dbFILE *f)
218     {
219     int errno_orig = errno;
220     if (f->fp)
221     fclose(f->fp);
222     if (f->mode == 'w' && *f->tempname)
223     remove(f->tempname);
224     free(f);
225     errno = errno_orig;
226     }
227    
228     /*************************************************************************/
229     /*************************************************************************/
230    
231     /* Read and write 2- and 4-byte quantities, pointers, and strings. All
232     * multibyte values are stored in big-endian order (most significant byte
233     * first). A pointer is stored as a byte, either 0 if NULL or 1 if not,
234     * and read pointers are returned as either (void *)0 or (void *)1. A
235     * string is stored with a 2-byte unsigned length (including the trailing
236     * \0) first; a length of 0 indicates that the string pointer is NULL.
237     * Written strings are truncated silently at 65534 bytes, and are always
238     * null-terminated.
239     *
240     * All routines return -1 on error, 0 otherwise.
241     */
242    
243     /*************************************************************************/
244    
245     int read_int8(int8 *ret, dbFILE *f)
246     {
247     int c = fgetc(f->fp);
248     if (c == EOF)
249     return -1;
250     *ret = c;
251     return 0;
252     }
253    
254     /* Alternative version of read_int8() to avoid GCC's pointer signedness
255     * warnings when reading into an unsigned variable: */
256     int read_uint8(uint8 *ret, dbFILE *f) {
257     return read_int8((int8 *)ret, f);
258     }
259    
260     int write_int8(int8 val, dbFILE *f)
261     {
262     if (fputc(val, f->fp) == EOF)
263     return -1;
264     return 0;
265     }
266    
267     /*************************************************************************/
268    
269     /* These are inline to help out {read,write}_string. */
270    
271     inline int read_int16(int16 *ret, dbFILE *f)
272     {
273     int c1, c2;
274    
275     c1 = fgetc(f->fp);
276     c2 = fgetc(f->fp);
277     if (c2 == EOF)
278     return -1;
279     *ret = c1<<8 | c2;
280     return 0;
281     }
282    
283     inline int read_uint16(uint16 *ret, dbFILE *f) {
284     return read_int16((int16 *)ret, f);
285     }
286    
287     inline int write_int16(int16 val, dbFILE *f)
288     {
289     fputc((val>>8), f->fp);
290     if (fputc(val, f->fp) == EOF)
291     return -1;
292     return 0;
293     }
294    
295     /*************************************************************************/
296    
297     int read_int32(int32 *ret, dbFILE *f)
298     {
299     int c1, c2, c3, c4;
300    
301     c1 = fgetc(f->fp);
302     c2 = fgetc(f->fp);
303     c3 = fgetc(f->fp);
304     c4 = fgetc(f->fp);
305     if (c4 == EOF)
306     return -1;
307     *ret = c1<<24 | c2<<16 | c3<<8 | c4;
308     return 0;
309     }
310    
311     int read_uint32(uint32 *ret, dbFILE *f) {
312     return read_int32((int32 *)ret, f);
313     }
314    
315     int write_int32(int32 val, dbFILE *f)
316     {
317     fputc((val>>24), f->fp);
318     fputc((val>>16), f->fp);
319     fputc((val>> 8), f->fp);
320     if (fputc((val & 0xFF), f->fp) == EOF)
321     return -1;
322     return 0;
323     }
324    
325     /*************************************************************************/
326    
327     int read_time(time_t *ret, dbFILE *f)
328     {
329     int32 high, low;
330     if (read_int32(&high, f) < 0 || read_int32(&low, f) < 0)
331     return -1;
332     #if SIZEOF_TIME_T > 4
333     *ret = (time_t)high << 32 | (time_t)low;
334     #else
335     *ret = low;
336     #endif
337     return 0;
338     }
339    
340     int write_time(time_t val, dbFILE *f)
341     {
342     #if SIZEOF_TIME_T > 4
343     if (write_int32(val>>32, f) < 0
344     || write_int32(val & (time_t)0xFFFFFFFF, f) < 0)
345     #else
346     if (write_int32(0, f) < 0 || write_int32(val, f) < 0)
347     #endif
348     return -1;
349     return 0;
350     }
351    
352     /*************************************************************************/
353    
354     int read_ptr(void **ret, dbFILE *f)
355     {
356     int c;
357    
358     c = fgetc(f->fp);
359     if (c == EOF)
360     return -1;
361     *ret = (c ? (void *)1 : (void *)0);
362     return 0;
363     }
364    
365     int write_ptr(const void *ptr, dbFILE *f)
366     {
367     if (fputc(ptr ? 1 : 0, f->fp) == EOF)
368     return -1;
369     return 0;
370     }
371    
372     /*************************************************************************/
373    
374     int read_string(char **ret, dbFILE *f)
375     {
376     char *s;
377     uint16 len;
378    
379     if (read_uint16(&len, f) < 0)
380     return -1;
381     if (len == 0) {
382     *ret = NULL;
383     return 0;
384     }
385     s = smalloc(len);
386     if (len != fread(s, 1, len, f->fp)) {
387     free(s);
388     return -1;
389     }
390     *ret = s;
391     return 0;
392     }
393    
394     int write_string(const char *s, dbFILE *f)
395     {
396     uint32 len;
397    
398     if (!s)
399     return write_int16(0, f);
400     len = strlen(s);
401     if (len > 65534)
402     len = 65534;
403     if (write_int16((uint16)(len+1), f) < 0)
404     return -1;
405     if (fwrite(s, 1, len, f->fp) != len)
406     return -1;
407     if (fputc(0, f->fp) == EOF)
408     return -1;
409     return 0;
410     }
411    
412     /*************************************************************************/
413    
414     /*
415     * Local variables:
416     * c-file-style: "stroustrup"
417     * c-file-offsets: ((case-label . *) (statement-case-intro . *))
418     * indent-tabs-mode: nil
419     * End:
420     *
421     * vim: expandtab shiftwidth=4:
422     */