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

# Content
1 /* 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 */