2001-05-23 15:26:42 +02:00
|
|
|
/*
|
|
|
|
* PROGRAM: JRD Rebuild scrambled database
|
2003-11-10 10:16:38 +01:00
|
|
|
* MODULE: rebuild.cpp
|
2001-05-23 15:26:42 +02:00
|
|
|
* DESCRIPTION: Main routine for analyzing and rebuilding database
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Interbase Public
|
|
|
|
* License Version 1.0 (the "License"); you may not use this file
|
|
|
|
* except in compliance with the License. You may obtain a copy
|
|
|
|
* of the License at http://www.Inprise.com/IPL.html
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an
|
|
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
|
|
|
* or implied. See the License for the specific language governing
|
|
|
|
* rights and limitations under the License.
|
|
|
|
*
|
|
|
|
* The Original Code was created by Inprise Corporation
|
|
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
|
|
* Copyright (C) Inprise Corporation.
|
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
* Contributor(s): ______________________________________.
|
|
|
|
*/
|
|
|
|
|
2002-06-29 10:49:39 +02:00
|
|
|
#include "../jrd/common.h"
|
|
|
|
|
2001-07-30 01:43:24 +02:00
|
|
|
#include "firebird.h"
|
2004-04-29 00:36:29 +02:00
|
|
|
#include <stdio.h>
|
2001-05-23 15:26:42 +02:00
|
|
|
#include <errno.h>
|
2003-08-25 09:26:39 +02:00
|
|
|
#include <string.h>
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
#include "../jrd/ibase.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/jrd.h"
|
2003-02-14 03:14:41 +01:00
|
|
|
#include "../jrd/jrd_time.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/pag.h"
|
|
|
|
#include "../jrd/tra.h"
|
2003-07-15 04:43:36 +02:00
|
|
|
#include "../utilities/rebuild/rebuild.h"
|
|
|
|
#include "../utilities/rebuild/rebui_proto.h"
|
|
|
|
#include "../utilities/rebuild/rmet_proto.h"
|
|
|
|
#include "../utilities/rebuild/rstor_proto.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/dmp_proto.h"
|
|
|
|
#include "../jrd/gds_proto.h"
|
|
|
|
|
|
|
|
#ifndef O_RDWR
|
|
|
|
#include <fcntl.h>
|
|
|
|
#endif
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
Database dbb_struct;
|
|
|
|
thread_db tdbb_struct, *gdbb;
|
2004-03-19 07:14:53 +01:00
|
|
|
PageControl dim;
|
2004-03-11 06:04:26 +01:00
|
|
|
jrd_tra dull;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
const ULONG* tips;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-04-29 00:36:29 +02:00
|
|
|
FILE* dbg_file;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-26 20:46:31 +02:00
|
|
|
static void checksum(RBDB, ULONG, ULONG, bool);
|
2001-05-23 15:26:42 +02:00
|
|
|
static USHORT compute_checksum(RBDB, PAG);
|
|
|
|
static void db_error(int);
|
2004-04-29 00:36:29 +02:00
|
|
|
static void dump(FILE *, RBDB, ULONG, ULONG, UCHAR);
|
|
|
|
static void dump_tips(FILE *, RBDB);
|
2004-02-20 07:43:27 +01:00
|
|
|
static void format_header(RBDB, header_page*, int, ULONG, ULONG, ULONG, ULONG);
|
2004-02-24 06:34:44 +01:00
|
|
|
static void format_index_root(index_root_page*, int, SSHORT, SSHORT);
|
2004-03-11 06:04:26 +01:00
|
|
|
static void format_pointer(pointer_page*, int, SSHORT, SSHORT, bool, SSHORT, SLONG *);
|
2004-02-20 07:43:27 +01:00
|
|
|
static void format_pip(page_inv_page*, int, int);
|
|
|
|
static void format_tip(tx_inv_page*, int, SLONG);
|
|
|
|
static void get_next_file(RBDB, header_page*);
|
2004-03-18 06:56:06 +01:00
|
|
|
static void get_range(TEXT***, const TEXT* const* const, ULONG*, ULONG*);
|
|
|
|
static void get_switch(TEXT**, SWC);
|
2003-12-31 06:36:12 +01:00
|
|
|
static void move(const SCHAR*, SCHAR*, SSHORT);
|
2004-02-20 07:43:27 +01:00
|
|
|
static header_page* open_database(RBDB, ULONG);
|
2004-04-29 00:36:29 +02:00
|
|
|
static void print_db_header(FILE*, const header_page*);
|
2001-05-23 15:26:42 +02:00
|
|
|
static void rebuild(RBDB);
|
2004-04-29 00:36:29 +02:00
|
|
|
static void write_headers(FILE*, RBDB, ULONG, ULONG);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-26 20:46:31 +02:00
|
|
|
static bool sw_rebuild;
|
|
|
|
static bool sw_print;
|
|
|
|
static bool sw_store;
|
|
|
|
static bool sw_dump_pages;
|
|
|
|
static bool sw_checksum;
|
|
|
|
static bool sw_fudge;
|
|
|
|
static bool sw_fix;
|
|
|
|
static bool sw_dump_tips;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
static const ULONG PPG_NUMBERS[] =
|
|
|
|
{
|
|
|
|
5897, 6058, 5409, 6199, 6200, 6220, 6221,
|
2001-05-23 15:26:42 +02:00
|
|
|
4739, 4868, 6332, 6333, 6329, 6359, 6751,
|
|
|
|
6331, 6392, 6806, 6819, 6820, 6866, 6875,
|
|
|
|
6876, 7019, 7284, 7430, 7431, 7757, 6893,
|
|
|
|
6894, 7408, 8308, 8309, 1036, 9120, 4528,
|
|
|
|
4563, 4572, 0, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2002-07-29 17:37:59 +02:00
|
|
|
int main( int argc, char *argv[])
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* m a i n
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Parse and interpret command line, then do a variety
|
|
|
|
* of things.
|
|
|
|
*
|
|
|
|
**************************************/
|
2004-03-11 06:04:26 +01:00
|
|
|
TEXT out_file[128];
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-04-29 00:36:29 +02:00
|
|
|
dbg_file = stdout;
|
2003-08-26 20:46:31 +02:00
|
|
|
sw_rebuild = sw_print = sw_store = sw_dump_pages = sw_checksum = false;
|
|
|
|
sw_dump_tips = sw_fudge = sw_fix = false;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
ULONG c_lower_bound, c_upper_bound, d_lower_bound, d_upper_bound,
|
|
|
|
p_lower_bound, p_upper_bound, pg_size;
|
2001-05-23 15:26:42 +02:00
|
|
|
pg_size = p_lower_bound = c_lower_bound = d_lower_bound = 0;
|
|
|
|
p_upper_bound = c_upper_bound = d_upper_bound = BIG_NUMBER;
|
2004-03-18 06:56:06 +01:00
|
|
|
USHORT pg_type = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
#ifdef VMS
|
|
|
|
argc = VMS_parse(&argv, argc);
|
|
|
|
#endif
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
const TEXT* const* const end = argv + argc;
|
2001-05-23 15:26:42 +02:00
|
|
|
++argv;
|
2004-03-18 06:56:06 +01:00
|
|
|
struct swc switch_space;
|
2004-03-11 06:04:26 +01:00
|
|
|
SWC token = &switch_space;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
RBDB rbdb = NULL;
|
|
|
|
header_page* header = NULL;
|
|
|
|
TEXT* ascii_out = NULL;
|
|
|
|
TEXT* db_in = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
while (argv < end) {
|
|
|
|
get_switch(argv, token);
|
|
|
|
argv++;
|
|
|
|
if (!token->swc_switch)
|
|
|
|
db_in = token->swc_string;
|
|
|
|
else {
|
|
|
|
switch (*token->swc_string) {
|
|
|
|
case 'b':
|
|
|
|
pg_size = atoi(*argv++);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'c':
|
2003-08-26 20:46:31 +02:00
|
|
|
sw_checksum = true;
|
2001-05-23 15:26:42 +02:00
|
|
|
get_range(&argv, end, &c_lower_bound, &c_upper_bound);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'd':
|
|
|
|
if ((argv < end) && (!strcmp(*argv, "tips"))) {
|
|
|
|
argv++;
|
2003-08-26 20:46:31 +02:00
|
|
|
sw_dump_tips = true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else
|
2003-08-26 20:46:31 +02:00
|
|
|
sw_dump_pages = true;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
get_range(&argv, end, &d_lower_bound, &d_upper_bound);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'f':
|
2003-08-26 20:46:31 +02:00
|
|
|
sw_fix = true;
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'o':
|
|
|
|
if (argv < end) {
|
|
|
|
get_switch(argv, token);
|
|
|
|
if (token->swc_switch)
|
|
|
|
break;
|
|
|
|
strcpy(out_file, token->swc_string);
|
|
|
|
ascii_out = out_file;
|
|
|
|
argv++;
|
|
|
|
}
|
|
|
|
case 'p':
|
2003-08-26 20:46:31 +02:00
|
|
|
sw_print = true;
|
2001-05-23 15:26:42 +02:00
|
|
|
get_range(&argv, end, &p_lower_bound, &p_upper_bound);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'r':
|
2003-08-26 20:46:31 +02:00
|
|
|
sw_rebuild = true;
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 's':
|
2003-08-26 20:46:31 +02:00
|
|
|
sw_store = true;
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 't':
|
|
|
|
pg_type = atoi(*argv++);
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (db_in) {
|
|
|
|
rbdb =
|
|
|
|
(RBDB)
|
|
|
|
RBDB_alloc((SLONG) (sizeof(struct rbdb) + strlen(db_in) + 1));
|
|
|
|
strcpy(rbdb->rbdb_file.fil_name, db_in);
|
|
|
|
rbdb->rbdb_file.fil_length = strlen(db_in);
|
|
|
|
if (header = open_database(rbdb, pg_size))
|
|
|
|
get_next_file(rbdb, header);
|
|
|
|
|
|
|
|
/* some systems don't care for this write sharing stuff... */
|
|
|
|
if (rbdb && (sw_dump_tips || sw_dump_pages)) {
|
|
|
|
RBDB_close(rbdb);
|
|
|
|
if (rbdb->rbdb_valid)
|
|
|
|
tips = RMET_tips(db_in);
|
|
|
|
RBDB_open(rbdb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
gdbb = &tdbb_struct;
|
|
|
|
gdbb->tdbb_database = &dbb_struct;
|
|
|
|
gdbb->tdbb_transaction = &dull;
|
|
|
|
dull.tra_number = header->hdr_next_transaction;
|
|
|
|
gdbb->tdbb_database->dbb_max_records = (rbdb->rbdb_page_size
|
2004-02-24 06:34:44 +01:00
|
|
|
- sizeof(struct data_page)) /
|
|
|
|
(sizeof(data_page::dpg_repeat) + OFFSETA(RHD, rhd_data));
|
2001-05-23 15:26:42 +02:00
|
|
|
gdbb->tdbb_database->dbb_pcontrol = &dim;
|
|
|
|
gdbb->tdbb_database->dbb_dp_per_pp = (rbdb->rbdb_page_size
|
2004-02-24 06:34:44 +01:00
|
|
|
- OFFSETA(pointer_page*, ppg_page)) * 8 / 34;
|
2001-05-23 15:26:42 +02:00
|
|
|
gdbb->tdbb_database->dbb_pcontrol->pgc_bytes = rbdb->rbdb_page_size
|
2004-02-20 07:43:27 +01:00
|
|
|
- OFFSETA(page_inv_page*, pip_bits);
|
2001-05-23 15:26:42 +02:00
|
|
|
gdbb->tdbb_database->dbb_pcontrol->pgc_ppp =
|
|
|
|
gdbb->tdbb_database->dbb_pcontrol->pgc_bytes * 8;
|
|
|
|
gdbb->tdbb_database->dbb_pcontrol->pgc_tpt =
|
2004-02-20 07:43:27 +01:00
|
|
|
(rbdb->rbdb_page_size - OFFSETA(tx_inv_page*, tip_transactions)) * 4;
|
2001-05-23 15:26:42 +02:00
|
|
|
gdbb->tdbb_database->dbb_pcontrol->pgc_pip = 1;
|
|
|
|
|
|
|
|
if (ascii_out)
|
2004-04-29 00:36:29 +02:00
|
|
|
dbg_file = fopen(ascii_out, "w");
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (sw_print && rbdb && header)
|
|
|
|
write_headers(dbg_file, rbdb, p_lower_bound, p_upper_bound);
|
|
|
|
|
|
|
|
if (sw_store && rbdb && header)
|
|
|
|
RSTORE(rbdb);
|
|
|
|
|
|
|
|
if (sw_checksum && rbdb && header)
|
|
|
|
checksum(rbdb, c_lower_bound, c_upper_bound, sw_fix);
|
|
|
|
|
|
|
|
if (sw_dump_tips && rbdb && header)
|
|
|
|
dump_tips(dbg_file, rbdb);
|
|
|
|
|
|
|
|
if (sw_dump_pages && rbdb && header)
|
|
|
|
dump(dbg_file, rbdb, d_lower_bound, d_upper_bound, pg_type);
|
|
|
|
|
|
|
|
if (sw_rebuild && rbdb && header)
|
|
|
|
rebuild(rbdb);
|
|
|
|
|
|
|
|
if (ascii_out)
|
2004-04-29 00:36:29 +02:00
|
|
|
fclose(dbg_file);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (rbdb)
|
|
|
|
RBDB_close(rbdb);
|
|
|
|
|
|
|
|
while (rbdb) {
|
2004-03-18 06:56:06 +01:00
|
|
|
RBDB next_db = rbdb->rbdb_next;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (rbdb->rbdb_buffer1)
|
|
|
|
gds__free(rbdb->rbdb_buffer1);
|
|
|
|
if (rbdb->rbdb_buffer2)
|
|
|
|
gds__free(rbdb->rbdb_buffer2);
|
|
|
|
gds__free(rbdb);
|
|
|
|
rbdb = next_db;
|
|
|
|
}
|
2002-07-29 17:37:59 +02:00
|
|
|
|
|
|
|
return 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-05-17 12:22:34 +02:00
|
|
|
#ifdef hpux
|
2003-02-10 14:28:35 +01:00
|
|
|
PAG CCH_fetch(WIN * x, USHORT y, int z)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* C C H _ f e t c h
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* This routine lets me link in
|
|
|
|
* DMP.C from JRD without having the
|
|
|
|
* linker piss all over it.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PAG CCH_release(WIN * x)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* C C H _ f e t c h
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* This routine lets me link in
|
|
|
|
* DMP.C from JRD without having the
|
|
|
|
* linker piss all over it.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
SCHAR *RBDB_alloc(SLONG size)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* R B D B _ a l l o c
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Allocate and zero a piece of memory.
|
|
|
|
*
|
|
|
|
**************************************/
|
2004-03-11 06:04:26 +01:00
|
|
|
char* const block = gds__alloc(size);
|
|
|
|
char* p = block;
|
|
|
|
do {
|
2001-05-23 15:26:42 +02:00
|
|
|
*p++ = 0;
|
2004-03-11 06:04:26 +01:00
|
|
|
} while (--size);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RBDB_close( RBDB rbdb)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* R B D B _ c l o s e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
for (; rbdb; rbdb = rbdb->rbdb_next)
|
|
|
|
close(rbdb->rbdb_file.fil_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RBDB_open( RBDB rbdb)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* R B D B _ o p e n
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Open a database file.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
if ((rbdb->rbdb_file.fil_file = open(rbdb->rbdb_file.fil_name, O_RDWR, 0))
|
|
|
|
== -1)
|
2004-03-11 06:04:26 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
db_error(errno);
|
2004-03-11 06:04:26 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PAG RBDB_read(RBDB rbdb, SLONG page_number)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* R B D B _ r e a d
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Read a database page.
|
|
|
|
*
|
|
|
|
**************************************/
|
2004-03-11 06:04:26 +01:00
|
|
|
int file = rbdb->rbdb_file.fil_file;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
const UINT64 offset = ((UINT64)page_number) * ((UINT64)rbdb->rbdb_page_size);
|
2002-06-29 10:49:39 +02:00
|
|
|
if (lseek (file, offset, 0) == -1)
|
2001-05-23 15:26:42 +02:00
|
|
|
db_error(errno);
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
SSHORT length = rbdb->rbdb_page_size;
|
|
|
|
for (char* p = (SCHAR *) rbdb->rbdb_buffer1;
|
|
|
|
length > 0;)
|
|
|
|
{
|
|
|
|
const SSHORT l = read(file, p, length);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (l < 0)
|
|
|
|
db_error(errno);
|
|
|
|
else if (l == 0)
|
|
|
|
return NULL;
|
|
|
|
p += l;
|
|
|
|
length -= l;
|
|
|
|
}
|
|
|
|
rbdb->rbdb_file.fil_file = file;
|
|
|
|
return rbdb->rbdb_buffer1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RBDB_write( RBDB rbdb, PAG page, SLONG page_number)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* R B D B _ w r i t e ( u n i x )
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Write a database page.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
page->pag_checksum = compute_checksum(rbdb, page);
|
2004-03-11 06:04:26 +01:00
|
|
|
const ULONG page_size = rbdb->rbdb_page_size;
|
|
|
|
int fd = rbdb->rbdb_file.fil_file;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
const UINT64 offset = ((UINT64)page_number) * ((UINT64)page_size);
|
2002-06-29 10:49:39 +02:00
|
|
|
if (lseek (fd, offset, 0) == -1)
|
2001-05-23 15:26:42 +02:00
|
|
|
db_error(errno);
|
|
|
|
if (write(fd, page, page_size) == -1)
|
|
|
|
db_error(errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-08-26 20:46:31 +02:00
|
|
|
static void checksum( RBDB rbdb, ULONG lower, ULONG upper, bool sw_fix)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* c h e c k s u m
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* read, compute, check, and correct
|
|
|
|
* checksums in this database.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
TEXT s[128];
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
for (ULONG page_number = lower; page_number <= upper; page_number++) {
|
|
|
|
pag* page = RBDB_read(rbdb, page_number);
|
|
|
|
if (!page)
|
2001-05-23 15:26:42 +02:00
|
|
|
return;
|
2004-03-11 06:04:26 +01:00
|
|
|
const USHORT old_checksum = page->pag_checksum;
|
|
|
|
const USHORT new_checksum = compute_checksum(rbdb, page);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (sw_fix)
|
|
|
|
page->pag_checksum = new_checksum;
|
|
|
|
if (new_checksum == old_checksum)
|
|
|
|
sprintf(s, "checksum %5d is OK", old_checksum);
|
|
|
|
else
|
|
|
|
sprintf(s, "stored checksum %5d\tcomputed checksum %5d\t%s",
|
|
|
|
old_checksum, new_checksum, (sw_fix) ? "fixed" : "");
|
2004-04-29 00:36:29 +02:00
|
|
|
printf("page %9d\t%s\n", page_number, s);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static USHORT compute_checksum( RBDB rbdb, PAG page)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* c o m p u t e _ c h e c k s u m
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* compute checksum for a V3 page.
|
|
|
|
*
|
|
|
|
**************************************/
|
2004-03-11 06:04:26 +01:00
|
|
|
const ULONG* const end = (ULONG *) ((SCHAR *) page + rbdb->rbdb_page_size);
|
|
|
|
const USHORT old_checksum = page->pag_checksum;
|
2001-05-23 15:26:42 +02:00
|
|
|
page->pag_checksum = 0;
|
2004-03-11 06:04:26 +01:00
|
|
|
const ULONG* p = (ULONG *) page;
|
|
|
|
ULONG checksum = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
do {
|
|
|
|
checksum += *p++;
|
|
|
|
checksum += *p++;
|
|
|
|
checksum += *p++;
|
|
|
|
checksum += *p++;
|
|
|
|
checksum += *p++;
|
|
|
|
checksum += *p++;
|
|
|
|
checksum += *p++;
|
|
|
|
checksum += *p++;
|
2004-03-11 06:04:26 +01:00
|
|
|
} while (p < end);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
page->pag_checksum = old_checksum;
|
|
|
|
|
|
|
|
if (checksum)
|
|
|
|
return checksum;
|
|
|
|
|
|
|
|
/* If the page is all zeros, return an artificial checksum */
|
|
|
|
|
|
|
|
for (p = (ULONG *) page; p < end;)
|
|
|
|
if (*p++)
|
|
|
|
return checksum;
|
|
|
|
|
|
|
|
/* Page is all zeros -- invent a checksum */
|
|
|
|
|
|
|
|
return 12345;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void db_error( int status)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* d b _ e r r o r
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
*
|
|
|
|
**************************************/
|
2004-04-29 00:36:29 +02:00
|
|
|
printf(strerror(status));
|
2001-05-23 15:26:42 +02:00
|
|
|
exit(FINI_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void dump(
|
2004-04-29 00:36:29 +02:00
|
|
|
FILE * file,
|
2001-05-23 15:26:42 +02:00
|
|
|
RBDB rbdb, ULONG lower, ULONG upper, UCHAR pg_type)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* d u m p
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* dump the contents of some page
|
|
|
|
* or pages in the database.
|
|
|
|
*
|
|
|
|
**************************************/
|
2004-03-11 06:04:26 +01:00
|
|
|
ULONG sequence = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (rbdb->rbdb_last_page && upper == BIG_NUMBER)
|
|
|
|
upper = rbdb->rbdb_last_page;
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
PAG page;
|
2001-05-23 15:26:42 +02:00
|
|
|
while (page = RBDB_read(rbdb, lower)) {
|
|
|
|
if (page->pag_type == pag_transactions && tips) {
|
2004-03-11 06:04:26 +01:00
|
|
|
for (const ULONG* tip = tips; tip[sequence]; sequence++) {
|
2001-05-23 15:26:42 +02:00
|
|
|
if (tip[sequence] == lower)
|
|
|
|
break;
|
|
|
|
else if (!tip[sequence]) {
|
|
|
|
sequence = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sequence = 0;
|
|
|
|
if (pg_type && (page->pag_type != pg_type)) {
|
2004-04-29 00:36:29 +02:00
|
|
|
printf("\nChanging page %d type from %d to %d\n", lower,
|
2001-05-23 15:26:42 +02:00
|
|
|
page->pag_type, pg_type);
|
|
|
|
page->pag_type = pg_type;
|
|
|
|
}
|
|
|
|
DMP_fetched_page(page, lower, sequence, rbdb->rbdb_page_size);
|
2004-03-11 06:04:26 +01:00
|
|
|
const ULONG* p = (ULONG *) page;
|
|
|
|
for (const ULONG* const end = p + (rbdb->rbdb_page_size / sizeof(ULONG)) - 1;
|
|
|
|
!*p && p < end; p++); // empty loop body
|
2001-05-23 15:26:42 +02:00
|
|
|
if (!*p)
|
2004-04-29 00:36:29 +02:00
|
|
|
printf(" Page is all zeroes.\n");
|
2001-05-23 15:26:42 +02:00
|
|
|
if (sw_fudge)
|
|
|
|
RBDB_write(rbdb, page, lower);
|
|
|
|
if (++lower > upper)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-04-29 00:36:29 +02:00
|
|
|
static void dump_tips( FILE * file, RBDB rbdb)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* d u m p _ t i p s
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* dump the contents of tip pages
|
|
|
|
* in the database.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
if (!tips)
|
2004-04-29 00:36:29 +02:00
|
|
|
printf("not enough database. Store headers and look there\n");
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
PAG page;
|
|
|
|
ULONG sequence = 1;
|
|
|
|
for (const ULONG* tip = tips; *tip && (page = RBDB_read(rbdb, *tip));
|
2001-05-23 15:26:42 +02:00
|
|
|
sequence++)
|
2004-03-11 06:04:26 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
DMP_fetched_page(page, *tip++, sequence, rbdb->rbdb_page_size);
|
2004-03-11 06:04:26 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void format_header(
|
|
|
|
RBDB rbdb,
|
2004-02-20 07:43:27 +01:00
|
|
|
header_page* page,
|
2001-05-23 15:26:42 +02:00
|
|
|
int page_size,
|
|
|
|
ULONG oldest, ULONG active, ULONG next, ULONG imp)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* f o r m a t _ h e a d e r
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Format a header page from inputs
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
memset(page, 0, page_size);
|
|
|
|
page->hdr_page_size = page_size;
|
2004-03-18 06:56:06 +01:00
|
|
|
page->pag_type = pag_header;
|
2004-08-23 22:44:49 +02:00
|
|
|
page->hdr_ods_version = ODS_VERSION | ODS_TYPE_CURRENT;
|
2001-05-23 15:26:42 +02:00
|
|
|
page->hdr_PAGES = 2;
|
|
|
|
page->hdr_oldest_transaction = oldest;
|
|
|
|
page->hdr_oldest_active = active;
|
|
|
|
page->hdr_next_transaction = next;
|
|
|
|
page->hdr_implementation = imp;
|
2004-03-18 06:56:06 +01:00
|
|
|
page->pag_checksum = compute_checksum(rbdb, page);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void format_index_root(
|
2004-02-24 06:34:44 +01:00
|
|
|
index_root_page* page,
|
2001-05-23 15:26:42 +02:00
|
|
|
int page_size, SSHORT relation_id, SSHORT count)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* f o r m a t _ i n d e x _ r o o t
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Format an index root page, without any indexes for a start
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
memset(page, 0, page_size);
|
2004-03-18 06:56:06 +01:00
|
|
|
page->pag_type = pag_root;
|
2001-05-23 15:26:42 +02:00
|
|
|
page->irt_relation = relation_id;
|
|
|
|
page->irt_count = count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void format_pointer(
|
2004-02-24 06:34:44 +01:00
|
|
|
pointer_page* page,
|
2001-05-23 15:26:42 +02:00
|
|
|
int page_size,
|
|
|
|
SSHORT relation_id,
|
|
|
|
SSHORT sequence,
|
2004-03-11 06:04:26 +01:00
|
|
|
bool eof, SSHORT count, SLONG * page_vector)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* f o r m a t _ p o i n t e r
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Format a pointer page. In addition of the buffer and page size,
|
|
|
|
* we need a relation id, a pointer page sequence (within relation),
|
|
|
|
* a flag indicated whether this is the last pointer page for the
|
|
|
|
* relation, and a count and vector of pages to point to.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
memset(page, 0, page_size);
|
2004-03-18 06:56:06 +01:00
|
|
|
page->pag_type = pag_pointer;
|
2001-05-23 15:26:42 +02:00
|
|
|
page->ppg_sequence = sequence;
|
|
|
|
page->ppg_relation = relation_id;
|
|
|
|
page->ppg_count = count;
|
|
|
|
page->ppg_min_space = 0;
|
|
|
|
page->ppg_max_space = count;
|
|
|
|
if (eof)
|
2004-03-18 06:56:06 +01:00
|
|
|
page->pag_flags |= ppg_eof;
|
2001-05-23 15:26:42 +02:00
|
|
|
memcpy(page->ppg_page, page_vector, count * sizeof(SLONG));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
static void format_pip( page_inv_page* page, int page_size, int last_flag)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* f o r m a t _ p i p
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Fake a fully RBDB_allocated (all pages RBDB_allocated) page inventory
|
|
|
|
* page.
|
|
|
|
*
|
|
|
|
**************************************/
|
2004-03-18 06:56:06 +01:00
|
|
|
page->pag_type = pag_pages;
|
|
|
|
page->pag_flags = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Set all page bits to zero, indicating RBDB_allocated */
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
const SSHORT bytes = page_size - OFFSETA(page_inv_page*, pip_bits);
|
2001-05-23 15:26:42 +02:00
|
|
|
memset(page->pip_bits, 0, bytes);
|
|
|
|
|
|
|
|
/* If this is the last pip, make sure the last page (which
|
|
|
|
will become the next pip) is marked free. When the
|
|
|
|
time comes to RBDB_allocate the next page, that page will
|
|
|
|
be formatted as the next pip. */
|
|
|
|
|
|
|
|
if (last_flag)
|
|
|
|
page->pip_bits[bytes - 1] |= 1 << 7;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
static void format_tip( tx_inv_page* page, int page_size, SLONG next_page)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* f o r m a t _ t i p
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Fake a fully commit transaction inventory page.
|
|
|
|
*
|
|
|
|
**************************************/
|
2004-03-18 06:56:06 +01:00
|
|
|
page->pag_type = pag_transactions;
|
|
|
|
page->pag_flags = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* The "next" tip page number is included for redundancy, but is not actually
|
|
|
|
read by the engine, so can be safely left zero. If known, it would nice
|
|
|
|
to supply it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
page->tip_next = next_page;
|
|
|
|
|
|
|
|
/* Code for committed transaction is 3, so just fill all
|
|
|
|
bytes with -1 */
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
const SSHORT bytes = page_size - OFFSETA(tx_inv_page*, tip_transactions);
|
2001-05-23 15:26:42 +02:00
|
|
|
memset(page->tip_transactions, -1, bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
static void get_next_file( RBDB rbdb, header_page* header)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* g e t _ n e x t _ f i l e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* If there's another file as part of
|
|
|
|
* this database, get it now.
|
|
|
|
*
|
|
|
|
**************************************/
|
2004-03-18 06:56:06 +01:00
|
|
|
RBDB* next = &rbdb->rbdb_next;
|
|
|
|
const UCHAR* p = header->hdr_data;
|
|
|
|
for (const UCHAR* const end = p + header->hdr_page_size;
|
2001-05-23 15:26:42 +02:00
|
|
|
p < end && *p != HDR_end; p += 2 + p[1])
|
2004-02-24 06:34:44 +01:00
|
|
|
{
|
|
|
|
if (*p == HDR_file)
|
|
|
|
{
|
2004-03-18 06:56:06 +01:00
|
|
|
RBDB next_rbdb = (RBDB) RBDB_alloc(sizeof(rbdb) + (SSHORT) p[1]);
|
2001-05-23 15:26:42 +02:00
|
|
|
next_rbdb->rbdb_file.fil_length = (SSHORT) p[1];
|
|
|
|
strncpy(next_rbdb->rbdb_file.fil_name, p + 2, (SSHORT) p[1]);
|
|
|
|
*next = next_rbdb;
|
|
|
|
next = &next_rbdb->rbdb_next;
|
|
|
|
break;
|
|
|
|
}
|
2004-02-24 06:34:44 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void get_range(
|
2004-03-18 06:56:06 +01:00
|
|
|
TEXT*** argv,
|
|
|
|
const TEXT* const* const end, ULONG* lower, ULONG* upper)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* g e t _ r a n g e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* get a range out of the argument
|
|
|
|
* vector;
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
struct swc token_space;
|
2004-03-18 06:56:06 +01:00
|
|
|
SWC token = &token_space;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (*argv < end) {
|
|
|
|
get_switch(*argv, token);
|
|
|
|
if (token->swc_switch)
|
|
|
|
return;
|
|
|
|
++*argv;
|
2004-03-18 06:56:06 +01:00
|
|
|
TEXT c = 0;
|
|
|
|
const TEXT* p;
|
2001-05-23 15:26:42 +02:00
|
|
|
for (p = token->swc_string; *p; p++)
|
|
|
|
if (*p < '0' || *p > '9') {
|
|
|
|
c = *p;
|
|
|
|
*p++ = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*upper = *lower = (ULONG) atoi(token->swc_string);
|
|
|
|
if (*p && (c == ':' || c == ',')) {
|
|
|
|
if (*p == '*')
|
|
|
|
*upper = BIG_NUMBER;
|
|
|
|
else
|
|
|
|
*upper = (ULONG) atoi(p);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (*argv < end) {
|
|
|
|
get_switch(*argv, token);
|
|
|
|
if (token->swc_switch)
|
|
|
|
return;
|
|
|
|
if ((*token->swc_string == ':') || (*token->swc_string == ',')) {
|
2004-03-18 06:56:06 +01:00
|
|
|
const TEXT* p = token->swc_string;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (*++p) {
|
|
|
|
if (*p == '*')
|
|
|
|
*upper = BIG_NUMBER;
|
|
|
|
else
|
|
|
|
*upper = (ULONG) atoi(p);
|
|
|
|
}
|
|
|
|
else if (*argv++ < end) {
|
|
|
|
get_switch(*argv, token);
|
|
|
|
if (token->swc_switch)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (*token->swc_string == '*')
|
|
|
|
*upper = BIG_NUMBER;
|
|
|
|
else
|
|
|
|
*upper = (ULONG) atoi(token->swc_string);
|
|
|
|
*argv++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
static void get_switch( TEXT** argv, SWC token)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* g e t _ s w i t c h
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* get the next argument in the argument
|
|
|
|
* vector;
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
token->swc_string = *argv;
|
|
|
|
|
|
|
|
if (*token->swc_string == '-') {
|
|
|
|
token->swc_switch = TRUE;
|
|
|
|
token->swc_string++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
token->swc_switch = FALSE;
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
const int temp = strlen(token->swc_string) - 1;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (token->swc_string[temp] == ',') {
|
|
|
|
token->swc_string[temp] = '\0';
|
|
|
|
token->swc_comma = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
token->swc_comma = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
static void move(const SCHAR* from, SCHAR* to, SSHORT length)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* m o v e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Move some stuff.
|
|
|
|
*
|
|
|
|
**************************************/
|
2003-12-31 06:36:12 +01:00
|
|
|
do {
|
2001-05-23 15:26:42 +02:00
|
|
|
*to++ = *from++;
|
2003-12-31 06:36:12 +01:00
|
|
|
} while (--length);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
static header_page* open_database( RBDB rbdb, ULONG pg_size)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* o p e n _ d a t a b a s e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Open a database and setup the
|
|
|
|
* rbdb. Return a pointer to the
|
|
|
|
* header page page header;
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
UCHAR temp[1024];
|
|
|
|
|
|
|
|
RBDB_open(rbdb);
|
|
|
|
|
|
|
|
rbdb->rbdb_page_size = sizeof(temp);
|
|
|
|
rbdb->rbdb_buffer1 = (PAG) temp;
|
|
|
|
rbdb->rbdb_valid = TRUE;
|
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
header_page* header = (header_page*) RBDB_read(rbdb, (SLONG) 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
if (header->pag_type != pag_header) {
|
2004-04-29 00:36:29 +02:00
|
|
|
printf("header page has wrong type, expected %d found %d!\n",
|
2004-03-18 06:56:06 +01:00
|
|
|
pag_header, header->pag_type);
|
2001-05-23 15:26:42 +02:00
|
|
|
rbdb->rbdb_valid = FALSE;
|
|
|
|
}
|
|
|
|
|
2004-08-23 22:44:49 +02:00
|
|
|
if (header->hdr_ods_version != ODS_VERSION | ODS_TYPE_CURRENT) {
|
|
|
|
printf("Wrong ODS version, expected %d type %04x, encountered %d type %04x.\n",
|
|
|
|
ODS_VERSION, ODS_TYPE_CURRENT,
|
|
|
|
header->hdr_ods_version & ~ODS_TYPE_MASK,
|
|
|
|
header->hdr_ods_version & ODS_TYPE_MASK
|
|
|
|
);
|
2001-05-23 15:26:42 +02:00
|
|
|
rbdb->rbdb_valid = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pg_size && (pg_size != header->hdr_page_size)) {
|
2004-04-29 00:36:29 +02:00
|
|
|
printf("Using page size %d\n", pg_size);
|
2001-05-23 15:26:42 +02:00
|
|
|
header->hdr_page_size = pg_size;
|
|
|
|
rbdb->rbdb_valid = FALSE;
|
|
|
|
}
|
|
|
|
else if (!header->hdr_page_size) {
|
2004-04-29 00:36:29 +02:00
|
|
|
printf("Using page size 1024\n");
|
2001-05-23 15:26:42 +02:00
|
|
|
header->hdr_page_size = 1024;
|
|
|
|
rbdb->rbdb_valid = FALSE;
|
|
|
|
}
|
|
|
|
|
2004-04-29 00:36:29 +02:00
|
|
|
printf("\nDatabase \"%s\"\n\n", rbdb->rbdb_file.fil_name);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
rbdb->rbdb_page_size = header->hdr_page_size;
|
|
|
|
rbdb->rbdb_map_count = rbdb->rbdb_map_length / rbdb->rbdb_page_size;
|
|
|
|
rbdb->rbdb_buffer1 = (PAG) RBDB_alloc(rbdb->rbdb_page_size);
|
|
|
|
rbdb->rbdb_buffer2 = (PAG) RBDB_alloc(rbdb->rbdb_page_size);
|
|
|
|
|
|
|
|
return header;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-04-29 00:36:29 +02:00
|
|
|
static void print_db_header( FILE* file, const header_page* header)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* p r i n t _ d b _ h e a d e r
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Print database header page.
|
|
|
|
*
|
|
|
|
**************************************/
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "Database header page information:\n");
|
|
|
|
fprintf(file, " Page size\t\t\t%d\n", header->hdr_page_size);
|
2004-08-23 22:44:49 +02:00
|
|
|
fprintf(file, " ODS version\t\t\t%d type %04x\n",
|
|
|
|
header->hdr_ods_version & ~ODS_TYPE_MASK,
|
|
|
|
header->hdr_ods_version & ODS_TYPE_MASK);
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, " PAGES\t\t\t%d\n", header->hdr_PAGES);
|
|
|
|
fprintf(file, " next page\t\t\t%d\n", header->hdr_next_page);
|
|
|
|
fprintf(file, " Oldest transaction\t\t%ld\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
header->hdr_oldest_transaction);
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, " Oldest active\t\t%ld\n", header->hdr_oldest_active);
|
|
|
|
fprintf(file, " Oldest snapshot\t\t%ld\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
header->hdr_oldest_snapshot);
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, " Next transaction\t\t%ld\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
header->hdr_next_transaction);
|
|
|
|
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, " Data pages per pointer page\t%ld\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
gdbb->tdbb_database->dbb_dp_per_pp);
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, " Max records per page\t%ld\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
gdbb->tdbb_database->dbb_max_records);
|
|
|
|
|
|
|
|
/*
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf (" Sequence number %d\n", header->hdr_sequence);
|
|
|
|
fprintf (" Creation date \n", header->hdr_creation_date);
|
2001-05-23 15:26:42 +02:00
|
|
|
*/
|
|
|
|
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, " Next attachment ID\t\t%ld\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
header->hdr_attachment_id);
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, " Implementation ID\t\t%ld\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
header->hdr_implementation);
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, " Shadow count\t\t%ld\n", header->hdr_shadow_count);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
tm time;
|
2003-11-08 17:40:17 +01:00
|
|
|
isc_decode_date(header->hdr_creation_date, &time);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, " Creation date:\t\t%s %d, %d %d:%02d:%02d\n",
|
2003-12-31 06:36:12 +01:00
|
|
|
FB_SHORT_MONTHS[time.tm_mon], time.tm_mday, time.tm_year + 1900,
|
2001-05-23 15:26:42 +02:00
|
|
|
time.tm_hour, time.tm_min, time.tm_sec);
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, " Cache buffers\t\t%ld\n", header->hdr_cache_buffers);
|
|
|
|
fprintf(file, " Bumped transaction\t\t%ld\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
header->hdr_bumped_transaction);
|
|
|
|
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\n Variable header data:\n");
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
SLONG number;
|
|
|
|
|
|
|
|
const UCHAR* p = header->hdr_data;
|
|
|
|
for (const UCHAR* const end = p + header->hdr_page_size;
|
2001-05-23 15:26:42 +02:00
|
|
|
p < end && *p != HDR_end; p += 2 + p[1])
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
switch (*p) {
|
|
|
|
case HDR_root_file_name:
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tRoot file name: %*s\n", p[1], p + 2);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HDR_journal_server:
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tJournal server: %*s\n", p[1], p + 2);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HDR_file:
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tContinuation file: %*s\n", p[1], p + 2);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HDR_last_page:
|
|
|
|
move(p + 2, &number, sizeof(number));
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tLast logical page: %ld\n", number);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HDR_unlicensed:
|
|
|
|
move(p + 2, &number, sizeof(number));
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tUnlicensed accesses: %ld\n", number);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HDR_sweep_interval:
|
|
|
|
move(p + 2, &number, sizeof(number));
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tSweep interval: %ld\n", number);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HDR_log_name:
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tReplay logging file: %*s\n", p[1], p + 2);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HDR_journal_file:
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tJournal file: %*s\n", p[1], p + 2);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HDR_password_file_key:
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tPassword file key: (can't print)\n");
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HDR_backup_info:
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tBackup info: (can't print)\n");
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HDR_cache_file:
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tShared cache file: %*s\n", p[1], p + 2);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tUnrecognized option %d, length %d\n", p[0],
|
2001-05-23 15:26:42 +02:00
|
|
|
p[1]);
|
|
|
|
}
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\n\n");
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void rebuild( RBDB rbdb)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* r e b u i l d
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Write out an improved database.
|
|
|
|
*
|
|
|
|
**************************************/
|
2003-12-31 06:36:12 +01:00
|
|
|
const ULONG page_size = rbdb->rbdb_page_size;
|
2004-03-18 06:56:06 +01:00
|
|
|
pag* page = rbdb->rbdb_buffer1;
|
2003-12-31 06:36:12 +01:00
|
|
|
const ULONG* page_numbers = PPG_NUMBERS;
|
|
|
|
for (ULONG number = 5898; (number < 5899) && (page = RBDB_read(rbdb, number));
|
|
|
|
number++)
|
|
|
|
{
|
2004-02-24 06:34:44 +01:00
|
|
|
pointer_page* pointer = (pointer_page*) page;
|
2004-03-11 06:04:26 +01:00
|
|
|
/* format_pointer (page, page_size, 25, 3, true, 37, page_numbers); */
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
RBDB_write(rbdb, page, number);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void write_headers(
|
2004-04-29 00:36:29 +02:00
|
|
|
FILE* file, RBDB rbdb, ULONG lower, ULONG upper)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* w r i t e _ h e a d e r s
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Print out the page headers.
|
|
|
|
*
|
|
|
|
**************************************/
|
2003-12-31 06:36:12 +01:00
|
|
|
pag* page;
|
|
|
|
for (ULONG page_number = lower;
|
2001-05-23 15:26:42 +02:00
|
|
|
(page_number <= upper) && (page = RBDB_read(rbdb, page_number));
|
2003-12-31 06:36:12 +01:00
|
|
|
page_number++)
|
|
|
|
{
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "page %d, ", page_number);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
switch (page->pag_type) {
|
|
|
|
case pag_header:
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "header page, checksum %d\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
page->pag_checksum);
|
2004-02-20 07:43:27 +01:00
|
|
|
print_db_header(file, (header_page*) page);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case pag_pages:
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "page inventory page, checksum %d\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
page->pag_checksum);
|
2004-02-20 07:43:27 +01:00
|
|
|
page_inv_page* pip = (page_inv_page*) page;
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tlowest free page %d\n\n", pip->pip_min);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case pag_transactions:
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "TIP page, checksum %d\n", page->pag_checksum);
|
|
|
|
fprintf(file, "\tnext tip for database %ld\n\n",
|
2004-02-20 07:43:27 +01:00
|
|
|
((tx_inv_page*) page)->tip_next);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case pag_pointer:
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "pointer page, checksum %d\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
page->pag_checksum);
|
2004-02-24 06:34:44 +01:00
|
|
|
const pointer_page* pointer = (pointer_page*) page;
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file,
|
2001-05-23 15:26:42 +02:00
|
|
|
"\trelation %d, sequence %ld, next pip %ld, active slots %d\n",
|
|
|
|
pointer->ppg_relation, pointer->ppg_sequence,
|
|
|
|
pointer->ppg_next, pointer->ppg_count);
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file,
|
2001-05-23 15:26:42 +02:00
|
|
|
"\tfirst slot with space %d, last slot with space %d\n",
|
|
|
|
pointer->ppg_min_space, pointer->ppg_max_space);
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\t%s\n",
|
2004-03-18 06:56:06 +01:00
|
|
|
(pointer->
|
2001-05-23 15:26:42 +02:00
|
|
|
pag_flags & ppg_eof) ? "last pointer for relation\n" :
|
|
|
|
"");
|
|
|
|
break;
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case pag_data:
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "data page, checksum %d\n", page->pag_checksum);
|
2004-02-24 06:34:44 +01:00
|
|
|
const data_page* data = (data_page*) page;
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file,
|
2001-05-23 15:26:42 +02:00
|
|
|
"\trelation %d, sequence %ld, records on page %d\n",
|
|
|
|
data->dpg_relation, data->dpg_sequence,
|
|
|
|
data->dpg_count);
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\t%s%s%s%s\n",
|
2004-03-18 06:56:06 +01:00
|
|
|
(data->
|
2001-05-23 15:26:42 +02:00
|
|
|
pag_flags & dpg_orphan) ? "orphan " : "",
|
2004-03-18 06:56:06 +01:00
|
|
|
(data->pag_flags & dpg_full) ? "full " : "",
|
|
|
|
(data->
|
2001-05-23 15:26:42 +02:00
|
|
|
pag_flags & dpg_large) ? "contains a large object" :
|
2004-03-18 06:56:06 +01:00
|
|
|
"", (data->pag_flags) ? "\n" : "");
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case pag_root:
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "index root page, checksum %d\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
page->pag_checksum);
|
2004-02-20 07:43:27 +01:00
|
|
|
const index_root_page* index_root = (index_root_page*) page;
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\trelation %d, number of indexes %d\n\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
index_root->irt_relation, index_root->irt_count);
|
|
|
|
break;
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case pag_index:
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "btree page (bucket), checksum %d\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
page->pag_checksum);
|
2004-02-24 06:34:44 +01:00
|
|
|
const btree_page* bucket = (btree_page*) page;
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\trelation %d, right sibling bucket: %ld,\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
bucket->btr_relation, bucket->btr_sibling);
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tdata length %d, index id %d, level %d\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
bucket->btr_length, bucket->btr_id, bucket->btr_level);
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\t%s%s%s\n",
|
2004-03-18 06:56:06 +01:00
|
|
|
(bucket->
|
2001-05-23 15:26:42 +02:00
|
|
|
pag_flags & btr_leftmost) ? "leftmost " : "",
|
2004-03-18 06:56:06 +01:00
|
|
|
(bucket->
|
2001-05-23 15:26:42 +02:00
|
|
|
pag_flags & btr_not_prop) ? "all duplicates " : "",
|
2004-03-18 06:56:06 +01:00
|
|
|
(bucket->
|
2001-05-23 15:26:42 +02:00
|
|
|
pag_flags & btr_marked) ? "marked for delete" : "");
|
|
|
|
break;
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case pag_blob:
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "blob page, checksum %d\n", page->pag_checksum);
|
2004-02-24 06:34:44 +01:00
|
|
|
const blob_page* blob = (blob_page*) page;
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tlead page: %ld, sequence: %ld, length: %d\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
blob->blp_lead_page, blob->blp_sequence,
|
|
|
|
blob->blp_length);
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "\tcontains %s\n",
|
2004-03-18 06:56:06 +01:00
|
|
|
(blob->
|
2001-05-23 15:26:42 +02:00
|
|
|
pag_flags & blp_pointers) ? "pointers" : "data");
|
|
|
|
break;
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case pag_ids:
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "generator page, checksum %d\n\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
page->pag_checksum);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case pag_log:
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(dbg_file, "write-ahead log info page, checksum %d\n\n",
|
2001-05-23 15:26:42 +02:00
|
|
|
page->pag_checksum);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2004-04-29 00:36:29 +02:00
|
|
|
fprintf(file, "unknown page type\n\n");
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2003-12-31 06:36:12 +01:00
|
|
|
|