1 |
/* Routines to load/save Services databases in standard format. |
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 |
#include "modules.h" |
12 |
#include "conffile.h" |
13 |
#include "databases.h" |
14 |
#include "encrypt.h" |
15 |
|
16 |
#include "fileutil.h" |
17 |
|
18 |
#define SAFE(x) do { if ((x) < 0) goto fail; } while (0) |
19 |
|
20 |
/*************************************************************************/ |
21 |
|
22 |
/* Database file format. |
23 |
* Note: all integer values are big-endian. |
24 |
* |
25 |
* File header: |
26 |
* 4 bytes -- version number (uint32) |
27 |
* 4 bytes -- size of header (uint32) |
28 |
* 4 bytes -- absolute offset to field list (uint32) |
29 |
* 4 bytes -- absolute offset to first record descriptor table (uint32) |
30 |
* |
31 |
* Field list: |
32 |
* 4 bytes -- size of field list (uint32) |
33 |
* 4 bytes -- number of fields (uint32) |
34 |
* 4 bytes -- size of a single record == sum of field sizes (uint32) |
35 |
* N bytes -- field definitions |
36 |
* |
37 |
* Field definition: |
38 |
* 4 bytes -- field data size in bytes |
39 |
* 2 bytes -- field data type (DBTYPE_*) (uint16) |
40 |
* 2 bytes -- length of field name, including trailing \0 (uint16) |
41 |
* N bytes -- field name |
42 |
* |
43 |
* Record descriptor table: |
44 |
* 4 bytes -- absolute offset to next table, or 0 if none (uint32) |
45 |
* 4 bytes -- size of this table in bytes |
46 |
* N bytes -- record entries |
47 |
* |
48 |
* Entry in record descriptor table: |
49 |
* 4 bytes -- absolute offset to record data |
50 |
* 4 bytes -- total length of record, including any strings |
51 |
* |
52 |
* Record data: |
53 |
* N bytes -- main record data |
54 |
* P bytes -- first string |
55 |
* Q bytes -- second string |
56 |
* ... |
57 |
* |
58 |
* Field data: |
59 |
* DBTYPE_INT8: 8-bit value |
60 |
* DBTYPE_UINT8: 8-bit value |
61 |
* DBTYPE_INT16: 16-bit value |
62 |
* DBTYPE_UINT16: 16-bit value |
63 |
* DBTYPE_INT32: 32-bit value |
64 |
* DBTYPE_UINT32: 32-bit value |
65 |
* DBTYPE_TIME: 64-bit value |
66 |
* DBTYPE_STRING: 32-bit offset to string from start of record |
67 |
* DBTYPE_BUFFER: N-byte buffer contents |
68 |
* DBTYPE_PASSWORD: 32-bit cipher string offset, (N-4) byte password buffer |
69 |
* |
70 |
* String data: |
71 |
* 2 bytes -- string length including trailing \0 (uint16), 0 if NULL |
72 |
* N bytes -- string contents |
73 |
*/ |
74 |
|
75 |
#define NEWDB_VERSION ('I'<<24 | 'S'<<16 | 'D'<<8 | 1) /* IRC Services DB */ |
76 |
#define RECTABLE_LEN 0x400 /* anything will do, really */ |
77 |
#define RECTABLE_SIZE (RECTABLE_LEN*8) |
78 |
|
79 |
/* Structure for keeping track of where to put data: */ |
80 |
typedef struct { |
81 |
const DBTable *table; |
82 |
int nfields; |
83 |
struct { |
84 |
DBField *field; |
85 |
int32 offset; /* -1 if not found on load */ |
86 |
int rawsize; /* native size on system */ |
87 |
int filesize; /* size when written to file */ |
88 |
} *fields; |
89 |
} TableInfo; |
90 |
|
91 |
/*************************************************************************/ |
92 |
/*********************** Internal helper routines ************************/ |
93 |
/*************************************************************************/ |
94 |
|
95 |
/* Helper routine: creates a TableInfo for a table. */ |
96 |
|
97 |
static TableInfo *create_tableinfo(const DBTable *table) |
98 |
{ |
99 |
TableInfo *ti; |
100 |
int i; |
101 |
|
102 |
ti = malloc(sizeof(*ti)); |
103 |
if (!ti) { |
104 |
module_log("create_tableinfo(): Out of memory for table %s", |
105 |
table->name); |
106 |
return NULL; |
107 |
} |
108 |
ti->table = table; |
109 |
for (i = 0; table->fields[i].name; i++) |
110 |
; |
111 |
ti->nfields = i; |
112 |
ti->fields = malloc(sizeof(*ti->fields) * ti->nfields); |
113 |
for (i = 0; i < ti->nfields; i++) { |
114 |
uint32 fieldsize; |
115 |
int rawsize = 0; |
116 |
ti->fields[i].field = &table->fields[i]; |
117 |
switch (ti->fields[i].field->type) { |
118 |
case DBTYPE_INT8: |
119 |
case DBTYPE_UINT8: fieldsize = 1; break; |
120 |
case DBTYPE_INT16: |
121 |
case DBTYPE_UINT16: fieldsize = 2; break; |
122 |
case DBTYPE_INT32: |
123 |
case DBTYPE_UINT32: fieldsize = 4; break; |
124 |
case DBTYPE_TIME: fieldsize = 8; rawsize = sizeof(time_t); break; |
125 |
case DBTYPE_STRING: fieldsize = 4; rawsize = sizeof(char *); break; |
126 |
case DBTYPE_BUFFER: fieldsize = ti->fields[i].field->length; break; |
127 |
case DBTYPE_PASSWORD: fieldsize = PASSMAX+4; |
128 |
rawsize = sizeof(Password); break; |
129 |
default: |
130 |
module_log("create_tableinfo(): Invalid type (%d) for field %s" |
131 |
" in table %s", ti->fields[i].field->type, |
132 |
ti->fields[i].field->name, ti->table->name); |
133 |
return NULL; |
134 |
} |
135 |
if (!rawsize) |
136 |
rawsize = fieldsize; |
137 |
ti->fields[i].rawsize = rawsize; |
138 |
ti->fields[i].filesize = fieldsize; |
139 |
ti->fields[i].offset = -1; |
140 |
} |
141 |
return ti; |
142 |
} |
143 |
|
144 |
/*************************************************************************/ |
145 |
|
146 |
/* Helper routine: frees a TableInfo. */ |
147 |
|
148 |
static void free_tableinfo(TableInfo *ti) |
149 |
{ |
150 |
if (ti) { |
151 |
free(ti->fields); |
152 |
free(ti); |
153 |
} |
154 |
} |
155 |
|
156 |
/*************************************************************************/ |
157 |
|
158 |
/* Helper routine: returns the filename for a table. */ |
159 |
|
160 |
static const char *make_filename(const DBTable *table) |
161 |
{ |
162 |
static const char okchars[] = /* characters allowed in filenames */ |
163 |
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; |
164 |
static char namebuf[1000]; |
165 |
const char *s; |
166 |
char *d = namebuf; |
167 |
|
168 |
for (s = table->name; *s && d-namebuf < sizeof(namebuf)-5; s++, d++) { |
169 |
if (strchr(okchars, *s)) |
170 |
*d = *s; |
171 |
else |
172 |
*d = '_'; |
173 |
} |
174 |
strcpy(d, ".sdb"); /* safe: space left for this above */ |
175 |
return namebuf; |
176 |
} |
177 |
|
178 |
/*************************************************************************/ |
179 |
/*********************** Database loading routines ***********************/ |
180 |
/*************************************************************************/ |
181 |
|
182 |
static int read_file_header(dbFILE *f, uint32 *fieldofs_ret, |
183 |
uint32 *recofs_ret); |
184 |
static int read_field_list(TableInfo *ti, dbFILE *f, uint32 *recsize_ret); |
185 |
static int read_records(TableInfo *ti, dbFILE *f, uint32 recsize); |
186 |
|
187 |
static int standard_load_table(DBTable *table) |
188 |
{ |
189 |
TableInfo *ti; |
190 |
const char *filename; |
191 |
dbFILE *f; |
192 |
uint32 fieldofs; /* field list offset */ |
193 |
uint32 recofs; /* record descriptor table offset */ |
194 |
uint32 recsize; /* size of a record, less strings */ |
195 |
|
196 |
/* Set up the TableInfo structure */ |
197 |
ti = create_tableinfo(table); |
198 |
if (!ti) |
199 |
return 0; |
200 |
|
201 |
/* Open the file */ |
202 |
filename = make_filename(table); |
203 |
f = open_db(filename, "r", NEWDB_VERSION); |
204 |
if (!f) { |
205 |
module_log_perror("Can't open %s for reading", filename); |
206 |
free_tableinfo(ti); |
207 |
return 0; |
208 |
} |
209 |
|
210 |
/* Check the file version and header size, and read header fields */ |
211 |
if (!read_file_header(f, &fieldofs, &recofs)) |
212 |
goto fail; |
213 |
|
214 |
/* Read in the field list and make sure it fits with the DBTable */ |
215 |
if (fseek(f->fp, fieldofs, SEEK_SET) < 0) { |
216 |
module_log_perror("Can't seek in %s", filename); |
217 |
goto fail; |
218 |
} |
219 |
if (!read_field_list(ti, f, &recsize)) |
220 |
goto fail; |
221 |
|
222 |
/* Read in the records */ |
223 |
if (fseek(f->fp, recofs, SEEK_SET) < 0) { |
224 |
module_log_perror("Can't seek in %s", filename); |
225 |
goto fail; |
226 |
} |
227 |
if (!read_records(ti, f, recsize)) |
228 |
goto fail; |
229 |
|
230 |
/* Call the postload routine, if any */ |
231 |
if (table->postload && !(*table->postload)()) { |
232 |
module_log_perror("Table %s postload routine failed", table->name); |
233 |
goto fail; |
234 |
} |
235 |
|
236 |
/* Done! */ |
237 |
close_db(f); |
238 |
free_tableinfo(ti); |
239 |
return 1; |
240 |
|
241 |
fail: |
242 |
close_db(f); |
243 |
free_tableinfo(ti); |
244 |
return 0; |
245 |
} |
246 |
|
247 |
/*************************************************************************/ |
248 |
|
249 |
/* Read the file header and verify the version and header size, then return |
250 |
* the field list offset and record descriptor table offset. |
251 |
*/ |
252 |
|
253 |
static int read_file_header(dbFILE *f, uint32 *fieldofs_ret, |
254 |
uint32 *recofs_ret) |
255 |
{ |
256 |
uint32 version; /* version number stored in file */ |
257 |
uint32 hdrsize; /* file header size */ |
258 |
|
259 |
SAFE(read_uint32(&version, f)); |
260 |
if (version != NEWDB_VERSION) { |
261 |
module_log("Bad version number on %s", f->filename); |
262 |
return 0; |
263 |
} |
264 |
SAFE(read_uint32(&hdrsize, f)); |
265 |
if (hdrsize < 16) { |
266 |
module_log("Bad header size on %s", f->filename); |
267 |
return 0; |
268 |
} |
269 |
SAFE(read_uint32(fieldofs_ret, f)); |
270 |
SAFE(read_uint32(recofs_ret, f)); |
271 |
return 1; |
272 |
|
273 |
fail: |
274 |
module_log("Read error on %s", f->filename); |
275 |
return 0; |
276 |
} |
277 |
|
278 |
/*************************************************************************/ |
279 |
|
280 |
/* Read in the field list, matching it with the field list in the DBTable |
281 |
* structure and filling in field offsets. The record size (as stored in |
282 |
* the file) is returned in *recsize_ret. |
283 |
*/ |
284 |
|
285 |
static int read_field_list(TableInfo *ti, dbFILE *f, uint32 *recsize_ret) |
286 |
{ |
287 |
uint32 thispos; /* current file position */ |
288 |
uint32 maxpos; /* end of table */ |
289 |
uint32 nfields; |
290 |
int32 recofs = 0; |
291 |
int i, j; |
292 |
|
293 |
SAFE(read_uint32(&maxpos, f)); /* field list size */ |
294 |
maxpos += ftell(f->fp)-4; |
295 |
SAFE(read_uint32(&nfields, f)); /* number of fields */ |
296 |
SAFE(read_uint32(recsize_ret, f)); /* record size--filled in later */ |
297 |
for (i = 0; i < nfields; i++) { |
298 |
uint32 length; |
299 |
uint16 type; |
300 |
uint16 strsize; |
301 |
char *fieldname; |
302 |
SAFE((int32)(thispos = ftell(f->fp))); |
303 |
SAFE(read_uint32(&length, f)); |
304 |
SAFE(read_uint16(&type, f)); |
305 |
SAFE(read_uint16(&strsize, f)); |
306 |
if (thispos+8+strsize > maxpos) { |
307 |
module_log("load_table(): premature end of field list"); |
308 |
return 0; |
309 |
} |
310 |
SAFE(fseek(f->fp, -2, SEEK_CUR)); |
311 |
SAFE(read_string(&fieldname, f)); |
312 |
if (!fieldname) { /* impossible */ |
313 |
module_log("load_table(): BUG: field name is NULL"); |
314 |
return 0; |
315 |
} |
316 |
for (j = 0; j < ti->nfields; j++) { |
317 |
DBField *field = ti->fields[j].field; |
318 |
if (type == field->type |
319 |
&& length == ti->fields[j].filesize |
320 |
&& strcmp(field->name,fieldname) == 0 |
321 |
) { |
322 |
ti->fields[j].offset = recofs; |
323 |
break; |
324 |
} |
325 |
} |
326 |
free(fieldname); |
327 |
recofs += length; |
328 |
} |
329 |
return 1; |
330 |
|
331 |
fail: |
332 |
module_log_perror("Error reading from %s", f->filename); |
333 |
return 0; |
334 |
} |
335 |
|
336 |
/*************************************************************************/ |
337 |
|
338 |
/* Read in table records. */ |
339 |
|
340 |
static int read_records(TableInfo *ti, dbFILE *f, uint32 recsize) |
341 |
{ |
342 |
uint32 *rectable; /* Table of record pointers and lengths */ |
343 |
int recnum; /* Record number within descriptor table */ |
344 |
int reccount; /* Record number in file */ |
345 |
void *record; /* Record pointer from first()/next() */ |
346 |
void *recbuf; /* Buffer for storing record data */ |
347 |
int i; |
348 |
|
349 |
/* Allocate record buffer */ |
350 |
recbuf = malloc(recsize); |
351 |
if (!recbuf) |
352 |
return 0; |
353 |
|
354 |
/* Variable init */ |
355 |
rectable = NULL; |
356 |
reccount = 0; |
357 |
record = NULL; |
358 |
|
359 |
/* Termination check has to be performed in the middle of the loop, |
360 |
* since we may need to read the record descriptor table first */ |
361 |
for (recnum = 0; ; recnum = (recnum+1) % (rectable[1]/8), reccount++) { |
362 |
|
363 |
if (!recnum) { /* Start of a new record descriptor table */ |
364 |
uint32 nextofs, tablesize; |
365 |
|
366 |
/* Seek to next table, if this isn't the first */ |
367 |
if (rectable) { |
368 |
if (!rectable[0]) { |
369 |
module_log("read_records(): %s: next table is 0!", |
370 |
f->filename); |
371 |
return 0; |
372 |
} |
373 |
SAFE(fseek(f->fp, rectable[0], SEEK_SET)); |
374 |
free(rectable); |
375 |
rectable = NULL; |
376 |
} |
377 |
|
378 |
/* Read in and check the `next' pointer and table size */ |
379 |
SAFE(read_uint32(&nextofs, f)); |
380 |
SAFE(read_uint32(&tablesize, f)); |
381 |
if (!tablesize) { |
382 |
module_log("read_records(): %s: rectable size is 0!", |
383 |
f->filename); |
384 |
return 0; |
385 |
} |
386 |
|
387 |
/* Allocate the table memory */ |
388 |
rectable = malloc(tablesize); |
389 |
if (!rectable) { |
390 |
module_log("read_records(): %s: no memory for rectable!", |
391 |
f->filename); |
392 |
return 0; |
393 |
} |
394 |
|
395 |
/* Read in the table */ |
396 |
rectable[0] = nextofs; |
397 |
rectable[1] = tablesize; |
398 |
for (i = 2; i < tablesize/4; i++) |
399 |
SAFE(read_uint32(&rectable[i], f)); |
400 |
|
401 |
/* First record in the table is at index 1 */ |
402 |
recnum++; |
403 |
} |
404 |
|
405 |
/* If this is the last record, bail out */ |
406 |
if (!rectable[recnum*2]) |
407 |
break; |
408 |
|
409 |
/* Make sure the record size is large enough */ |
410 |
if (rectable[recnum*2+1] < recsize) { |
411 |
module_log("read_records(): %s: record %d is too small," |
412 |
" skipping", f->filename, reccount); |
413 |
continue; |
414 |
} |
415 |
|
416 |
/* Allocate a new record */ |
417 |
record = ti->table->newrec(); |
418 |
if (!record) { |
419 |
module_log("read_records(): %s: newrec() failed for record %d!", |
420 |
f->filename, reccount); |
421 |
return 0; |
422 |
} |
423 |
|
424 |
/* Seek to the location of this record and read the main record data */ |
425 |
SAFE(fseek(f->fp, rectable[recnum*2], SEEK_SET)); |
426 |
if (fread(recbuf, recsize, 1, f->fp) != 1) |
427 |
goto fail; |
428 |
|
429 |
/* Read fields from record buffer and strings from file */ |
430 |
for (i = 0; i < ti->nfields; i++) { |
431 |
const DBField *field = ti->fields[i].field; |
432 |
const void *src = (const int8 *)recbuf + ti->fields[i].offset; |
433 |
if (ti->fields[i].offset < 0) /* field not present in file */ |
434 |
continue; |
435 |
if (field->type == DBTYPE_STRING) { |
436 |
uint32 strofs; |
437 |
char *string; |
438 |
strofs = ((uint8 *)src)[0] << 24 |
439 |
| ((uint8 *)src)[1] << 16 |
440 |
| ((uint8 *)src)[2] << 8 |
441 |
| ((uint8 *)src)[3]; |
442 |
if (strofs >= rectable[recnum*2+1]) { |
443 |
module_log("read_records(): %s: string for field `%s' of" |
444 |
" record %d is out of range, skipping", |
445 |
f->filename, field->name, reccount); |
446 |
continue; |
447 |
} |
448 |
SAFE(fseek(f->fp, rectable[recnum*2] + strofs, SEEK_SET)); |
449 |
SAFE(read_string(&string, f)); |
450 |
put_dbfield(record, field, &string); |
451 |
} else if (field->type == DBTYPE_PASSWORD) { |
452 |
uint32 strofs; |
453 |
Password pass; |
454 |
char *cipher; |
455 |
strofs = ((uint8 *)src)[0] << 24 |
456 |
| ((uint8 *)src)[1] << 16 |
457 |
| ((uint8 *)src)[2] << 8 |
458 |
| ((uint8 *)src)[3]; |
459 |
if (strofs >= rectable[recnum*2+1]) { |
460 |
module_log("read_records(): %s: string for field `%s' of" |
461 |
" record %d is out of range, skipping", |
462 |
f->filename, field->name, reccount); |
463 |
continue; |
464 |
} |
465 |
SAFE(fseek(f->fp, rectable[recnum*2] + strofs, SEEK_SET)); |
466 |
SAFE(read_string((char **)&cipher, f)); |
467 |
init_password(&pass); |
468 |
set_password(&pass, src+4, cipher); |
469 |
free(cipher); |
470 |
put_dbfield(record, field, &pass); |
471 |
} else if (field->type == DBTYPE_BUFFER) { |
472 |
put_dbfield(record, field, src); |
473 |
} else { |
474 |
char fieldbuf[16]; /* Big enough for any int or time type */ |
475 |
switch (field->type) { |
476 |
case DBTYPE_INT8: |
477 |
case DBTYPE_UINT8: |
478 |
*((uint8 *)fieldbuf) = ((uint8 *)src)[0]; |
479 |
break; |
480 |
case DBTYPE_INT16: |
481 |
case DBTYPE_UINT16: |
482 |
*((uint16 *)fieldbuf) = ((uint8 *)src)[0] << 8 |
483 |
| ((uint8 *)src)[1]; |
484 |
break; |
485 |
case DBTYPE_INT32: |
486 |
case DBTYPE_UINT32: |
487 |
*((uint32 *)fieldbuf) = ((uint8 *)src)[0] << 24 |
488 |
| ((uint8 *)src)[1] << 16 |
489 |
| ((uint8 *)src)[2] << 8 |
490 |
| ((uint8 *)src)[3]; |
491 |
break; |
492 |
case DBTYPE_TIME: |
493 |
*((time_t *)fieldbuf) = ((uint8 *)src)[4] << 24 |
494 |
| ((uint8 *)src)[5] << 16 |
495 |
| ((uint8 *)src)[6] << 8 |
496 |
| ((uint8 *)src)[7]; |
497 |
#if SIZEOF_TIME_T >= 8 |
498 |
*((time_t *)fieldbuf) |=(time_t)((uint8 *)src)[0] << 56 |
499 |
| (time_t)((uint8 *)src)[1] << 48 |
500 |
| (time_t)((uint8 *)src)[2] << 40 |
501 |
| (time_t)((uint8 *)src)[3] << 32; |
502 |
#endif |
503 |
break; |
504 |
case DBTYPE_STRING: |
505 |
case DBTYPE_BUFFER: |
506 |
case DBTYPE_PASSWORD: |
507 |
/* Handled above--listed here to avoid warnings */ |
508 |
break; |
509 |
} |
510 |
put_dbfield(record, field, fieldbuf); |
511 |
} |
512 |
} /* for each field */ |
513 |
|
514 |
/* Add record to table */ |
515 |
ti->table->insert(record); |
516 |
record = NULL; |
517 |
|
518 |
} /* for each record */ |
519 |
|
520 |
/* All done */ |
521 |
free(recbuf); |
522 |
free(rectable); |
523 |
return 1; |
524 |
|
525 |
fail: |
526 |
module_log_perror("read_records(): Read error on %s", f->filename); |
527 |
if (record) |
528 |
ti->table->freerec(record); |
529 |
free(recbuf); |
530 |
free(rectable); |
531 |
return 0; |
532 |
} |
533 |
|
534 |
/*************************************************************************/ |
535 |
/*********************** Database saving routines ************************/ |
536 |
/*************************************************************************/ |
537 |
|
538 |
static int write_file_header(TableInfo *ti, dbFILE *f); |
539 |
static int write_field_list(TableInfo *ti, dbFILE *f, uint32 *recsize_ret); |
540 |
static int write_records(TableInfo *ti, dbFILE *f, uint32 recsize); |
541 |
|
542 |
static int standard_save_table(DBTable *table) |
543 |
{ |
544 |
TableInfo *ti; |
545 |
const char *filename; |
546 |
dbFILE *f; |
547 |
uint32 recsize; /* size of a record, less strings */ |
548 |
|
549 |
/* Set up the TableInfo structure */ |
550 |
ti = create_tableinfo(table); |
551 |
if (!ti) |
552 |
return 0; |
553 |
|
554 |
/* Open the file */ |
555 |
filename = make_filename(table); |
556 |
f = open_db(filename, "w", NEWDB_VERSION); |
557 |
if (!f) { |
558 |
module_log_perror("Can't open %s for writing", filename); |
559 |
free_tableinfo(ti); |
560 |
return 0; |
561 |
} |
562 |
|
563 |
/* Write the file header */ |
564 |
SAFE(write_file_header(ti, f)); |
565 |
|
566 |
/* Write the field list */ |
567 |
SAFE(write_field_list(ti, f, &recsize)); |
568 |
|
569 |
/* Write the actual records */ |
570 |
SAFE(write_records(ti, f, recsize)); |
571 |
|
572 |
/* Done! */ |
573 |
close_db(f); |
574 |
free_tableinfo(ti); |
575 |
return 1; |
576 |
|
577 |
fail: |
578 |
module_log_perror("Can't write to %s", filename); |
579 |
restore_db(f); |
580 |
free_tableinfo(ti); |
581 |
return 0; |
582 |
} |
583 |
|
584 |
/*************************************************************************/ |
585 |
|
586 |
/* Write the file header. */ |
587 |
|
588 |
static int write_file_header(TableInfo *ti, dbFILE *f) |
589 |
{ |
590 |
/* Note that the version number is already written */ |
591 |
SAFE(write_int32(16, f)); /* header size */ |
592 |
SAFE(write_int32(0, f)); /* field list offset--filled in later */ |
593 |
SAFE(write_int32(0, f)); /* record desc. table offset--filled in later */ |
594 |
return 0; |
595 |
fail: |
596 |
return -1; |
597 |
} |
598 |
|
599 |
/*************************************************************************/ |
600 |
|
601 |
/* Write the field list, filling in ti->fields[].offset as we go, and send |
602 |
* back the size of a record; also fills in field list offset in file |
603 |
* header. |
604 |
*/ |
605 |
|
606 |
static int write_field_list(TableInfo *ti, dbFILE *f, uint32 *recsize_ret) |
607 |
{ |
608 |
uint32 listpos; /* file position of start of table */ |
609 |
uint32 endpos; /* file position of end of table */ |
610 |
uint32 recsize = 0; |
611 |
int i, nfields = 0; |
612 |
|
613 |
SAFE((int32)(listpos = ftell(f->fp))); |
614 |
SAFE(fseek(f->fp, 8, SEEK_SET)); |
615 |
SAFE(write_int32(listpos, f)); |
616 |
SAFE(fseek(f->fp, listpos, SEEK_SET)); |
617 |
SAFE(write_int32(0, f)); /* field list size--filled in later*/ |
618 |
SAFE(write_int32(0, f)); /* number of fields--filled in later */ |
619 |
SAFE(write_int32(0, f)); /* record size--filled in later */ |
620 |
for (i = 0; i < ti->nfields; i++) { |
621 |
if (ti->fields[i].field->load_only) |
622 |
continue; |
623 |
SAFE(write_int32(ti->fields[i].filesize, f)); |
624 |
SAFE(write_int16(ti->fields[i].field->type, f)); |
625 |
SAFE(write_string(ti->fields[i].field->name, f)); |
626 |
ti->fields[i].offset = recsize; |
627 |
nfields++; |
628 |
recsize += ti->fields[i].filesize; |
629 |
} |
630 |
SAFE((int32)(endpos = ftell(f->fp))); |
631 |
SAFE(fseek(f->fp, listpos, SEEK_SET)); |
632 |
SAFE(write_int32(endpos-listpos, f)); |
633 |
SAFE(write_int32(nfields, f)); |
634 |
SAFE(write_int32(recsize, f)); |
635 |
SAFE(fseek(f->fp, endpos, SEEK_SET)); |
636 |
*recsize_ret = recsize; |
637 |
return 0; |
638 |
fail: |
639 |
return -1; |
640 |
} |
641 |
|
642 |
/*************************************************************************/ |
643 |
|
644 |
/* Write out table records. */ |
645 |
|
646 |
static int write_records(TableInfo *ti, dbFILE *f, uint32 recsize) |
647 |
{ |
648 |
uint32 listpos; /* Offset of current record descriptor table */ |
649 |
int recnum; /* Record number within table */ |
650 |
const void *record; /* Record pointer from first()/next() */ |
651 |
void *recbuf = NULL; /* Buffer for storing record data */ |
652 |
int i; |
653 |
|
654 |
/* Allocate record buffer */ |
655 |
recbuf = malloc(recsize); |
656 |
if (!recbuf) |
657 |
return -1; |
658 |
|
659 |
/* Variable init */ |
660 |
listpos = 12; /* First record descriptor table pointer is recorded here */ |
661 |
recnum = 0; |
662 |
|
663 |
/* Note the lack of a termination condition in this loop--we need to |
664 |
* write a 0 to terminate the record list, which may involve creating a |
665 |
* new descriptor table, so we let the first part of the loop run and |
666 |
* break out in the middle when we hit the end of the table. */ |
667 |
for (record = ti->table->first(); ; record = ti->table->next()) { |
668 |
uint32 thispos; /* Offset this record is written at */ |
669 |
uint32 nextpos; /* Temp variable to hold next record's offset */ |
670 |
|
671 |
if (!recnum) { /* Start of a new rectable */ |
672 |
uint32 oldpos = listpos; |
673 |
static char dummy_rectable[RECTABLE_SIZE]; |
674 |
|
675 |
/* Seek to a multiple of the table size, just for the hell of it */ |
676 |
SAFE((int32)(listpos = ftell(f->fp))); |
677 |
listpos = (listpos + (RECTABLE_SIZE-1)) |
678 |
/ RECTABLE_SIZE * RECTABLE_SIZE; |
679 |
SAFE(fseek(f->fp, listpos, SEEK_SET)); |
680 |
|
681 |
/* Write out an empty descriptor table */ |
682 |
SAFE(write_buffer(dummy_rectable, f)); |
683 |
SAFE(fseek(f->fp, listpos, SEEK_SET)); |
684 |
|
685 |
/* Write the `next' pointer (currently 0) and size */ |
686 |
SAFE(write_int32(0, f)); |
687 |
SAFE(write_int32(RECTABLE_SIZE, f)); |
688 |
|
689 |
/* Write the new table offset into the old `next' pointer */ |
690 |
SAFE(fseek(f->fp, oldpos, SEEK_SET)); |
691 |
SAFE(write_int32(listpos, f)); |
692 |
|
693 |
/* Seek to where the next record should be written */ |
694 |
SAFE(fseek(f->fp, listpos + RECTABLE_SIZE, SEEK_SET)); |
695 |
|
696 |
/* The first record is written at index 1 (don't smash the |
697 |
* next pointer) */ |
698 |
recnum = 1; |
699 |
} |
700 |
|
701 |
/* If this is the last record, record a 0 to terminate the record |
702 |
* list and bail out */ |
703 |
if (!record) { |
704 |
SAFE(fseek(f->fp, listpos + recnum*8, SEEK_SET)); |
705 |
SAFE(write_int32(0, f)); |
706 |
SAFE(write_int32(0, f)); |
707 |
break; |
708 |
} |
709 |
|
710 |
/* Save the location of this record */ |
711 |
SAFE((int32)(thispos = ftell(f->fp))); |
712 |
|
713 |
/* Seek to end of record, for writing strings */ |
714 |
SAFE(fseek(f->fp, thispos + recsize, SEEK_SET)); |
715 |
|
716 |
/* Write fields to record buffer and strings to file */ |
717 |
for (i = 0; i < ti->nfields; i++) { |
718 |
const DBField *field = ti->fields[i].field; |
719 |
uint8 *dest = (uint8 *)recbuf + ti->fields[i].offset; |
720 |
if (field->load_only) |
721 |
continue; |
722 |
if (field->type == DBTYPE_STRING) { |
723 |
const char *string; |
724 |
uint32 strofs; |
725 |
get_dbfield(record, field, (void *)&string); |
726 |
SAFE((int32)(strofs = ftell(f->fp))); |
727 |
strofs -= thispos; |
728 |
dest[0] = strofs >> 24; |
729 |
dest[1] = strofs >> 16; |
730 |
dest[2] = strofs >> 8; |
731 |
dest[3] = strofs; |
732 |
SAFE(write_string(string, f)); |
733 |
} else if (field->type == DBTYPE_PASSWORD) { |
734 |
Password pass; |
735 |
uint32 strofs; |
736 |
get_dbfield(record, field, (void *)&pass); |
737 |
SAFE((int32)(strofs = ftell(f->fp))); |
738 |
strofs -= thispos; |
739 |
dest[0] = strofs >> 24; |
740 |
dest[1] = strofs >> 16; |
741 |
dest[2] = strofs >> 8; |
742 |
dest[3] = strofs; |
743 |
SAFE(write_string(pass.cipher, f)); |
744 |
memcpy(dest+4, pass.password, PASSMAX); |
745 |
} else if (field->type == DBTYPE_BUFFER) { |
746 |
get_dbfield(record, field, dest); |
747 |
} else { |
748 |
uint8 fieldbuf[16]; /* Big enough for any int or time type */ |
749 |
get_dbfield(record, field, fieldbuf); |
750 |
switch (field->type) { |
751 |
case DBTYPE_INT8: |
752 |
case DBTYPE_UINT8: |
753 |
dest[0] = *((uint8 *)fieldbuf); |
754 |
break; |
755 |
case DBTYPE_INT16: |
756 |
case DBTYPE_UINT16: |
757 |
dest[0] = *((uint16 *)fieldbuf) >> 8; |
758 |
dest[1] = *((uint16 *)fieldbuf); |
759 |
break; |
760 |
case DBTYPE_INT32: |
761 |
case DBTYPE_UINT32: |
762 |
dest[0] = *((uint32 *)fieldbuf) >> 24; |
763 |
dest[1] = *((uint32 *)fieldbuf) >> 16; |
764 |
dest[2] = *((uint32 *)fieldbuf) >> 8; |
765 |
dest[3] = *((uint32 *)fieldbuf); |
766 |
break; |
767 |
case DBTYPE_TIME: |
768 |
#if SIZEOF_TIME_T >= 8 |
769 |
dest[0] = *((time_t *)fieldbuf) >> 56; |
770 |
dest[1] = *((time_t *)fieldbuf) >> 48; |
771 |
dest[2] = *((time_t *)fieldbuf) >> 40; |
772 |
dest[3] = *((time_t *)fieldbuf) >> 32; |
773 |
#else |
774 |
/* Copy the sign bit into the four high bytes */ |
775 |
dest[0] = (int32) *((time_t *)fieldbuf) >> 31; |
776 |
dest[1] = (int32) *((time_t *)fieldbuf) >> 31; |
777 |
dest[2] = (int32) *((time_t *)fieldbuf) >> 31; |
778 |
dest[3] = (int32) *((time_t *)fieldbuf) >> 31; |
779 |
#endif |
780 |
dest[4] = *((time_t *)fieldbuf) >> 24; |
781 |
dest[5] = *((time_t *)fieldbuf) >> 16; |
782 |
dest[6] = *((time_t *)fieldbuf) >> 8; |
783 |
dest[7] = *((time_t *)fieldbuf); |
784 |
break; |
785 |
case DBTYPE_STRING: |
786 |
case DBTYPE_BUFFER: |
787 |
case DBTYPE_PASSWORD: |
788 |
/* Handled above--listed here to avoid warnings */ |
789 |
break; |
790 |
} |
791 |
} |
792 |
} /* for each field */ |
793 |
|
794 |
/* Save the location of the next record */ |
795 |
SAFE((int32)(nextpos = ftell(f->fp))); |
796 |
|
797 |
/* Write record pointer and length to table */ |
798 |
SAFE(fseek(f->fp, listpos + recnum*8, SEEK_SET)); |
799 |
SAFE(write_int32(thispos, f)); |
800 |
SAFE(write_int32(nextpos-thispos, f)); |
801 |
|
802 |
/* Write record buffer to file */ |
803 |
SAFE(fseek(f->fp, thispos, SEEK_SET)); |
804 |
if (fwrite(recbuf, recsize, 1, f->fp) != 1) |
805 |
goto fail; |
806 |
|
807 |
/* Seek to next record's starting position */ |
808 |
SAFE(fseek(f->fp, nextpos, SEEK_SET)); |
809 |
|
810 |
/* Move to the next record index */ |
811 |
recnum = (recnum+1) % RECTABLE_LEN; |
812 |
} |
813 |
|
814 |
/* All done */ |
815 |
free(recbuf); |
816 |
return 0; |
817 |
|
818 |
fail: |
819 |
free(recbuf); |
820 |
return -1; |
821 |
} |
822 |
|
823 |
/*************************************************************************/ |
824 |
/***************************** Module stuff ******************************/ |
825 |
/*************************************************************************/ |
826 |
|
827 |
#ifndef INCLUDE_IN_VERSION4 |
828 |
|
829 |
ConfigDirective module_config[] = { |
830 |
{ NULL } |
831 |
}; |
832 |
|
833 |
|
834 |
static DBModule dbmodule_standard = { |
835 |
.load_table = standard_load_table, |
836 |
.save_table = standard_save_table, |
837 |
}; |
838 |
|
839 |
/*************************************************************************/ |
840 |
|
841 |
int init_module(void) |
842 |
{ |
843 |
if (!register_dbmodule(&dbmodule_standard)) { |
844 |
module_log("Unable to register module with database core"); |
845 |
exit_module(0); |
846 |
return 0; |
847 |
} |
848 |
|
849 |
return 1; |
850 |
} |
851 |
|
852 |
/*************************************************************************/ |
853 |
|
854 |
int exit_module(int shutdown) |
855 |
{ |
856 |
unregister_dbmodule(&dbmodule_standard); |
857 |
return 1; |
858 |
} |
859 |
|
860 |
/*************************************************************************/ |
861 |
|
862 |
#endif /* !INCLUDE_IN_VERSION4 */ |
863 |
|
864 |
/* |
865 |
* Local variables: |
866 |
* c-file-style: "stroustrup" |
867 |
* c-file-offsets: ((case-label . *) (statement-case-intro . *)) |
868 |
* indent-tabs-mode: nil |
869 |
* End: |
870 |
* |
871 |
* vim: expandtab shiftwidth=4: |
872 |
*/ |