8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-31 23:23:04 +01:00
firebird-mirror/src/utilities/rebuild/rebuild.cpp

1234 lines
30 KiB
C++
Raw Normal View History

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"
#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>
#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"
#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
Database dbb_struct;
thread_db tdbb_struct, *gdbb;
PageControl dim;
jrd_tra dull;
2001-05-23 15:26:42 +02: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);
static void format_header(RBDB, header_page*, int, ULONG, ULONG, ULONG, ULONG);
static void format_index_root(index_root_page*, int, SSHORT, SSHORT);
static void format_pointer(pointer_page*, int, SSHORT, SSHORT, bool, SSHORT, SLONG *);
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*);
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);
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
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
};
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.
*
**************************************/
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
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;
USHORT pg_type = 0;
2001-05-23 15:26:42 +02:00
#ifdef VMS
argc = VMS_parse(&argv, argc);
#endif
const TEXT* const* const end = argv + argc;
2001-05-23 15:26:42 +02:00
++argv;
struct swc switch_space;
SWC token = &switch_space;
2001-05-23 15:26:42 +02: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
- 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
- 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
- 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 =
(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) {
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;
}
return 0;
2001-05-23 15:26:42 +02:00
}
2007-11-12 16:18:49 +01:00
#ifdef HPUX
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.
*
**************************************/
char* const block = gds__alloc(size);
char* p = block;
do {
2001-05-23 15:26:42 +02:00
*p++ = 0;
} 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)
{
2001-05-23 15:26:42 +02:00
db_error(errno);
}
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.
*
**************************************/
int file = rbdb->rbdb_file.fil_file;
2001-05-23 15:26:42 +02:00
2007-11-17 01:38:16 +01:00
const FB_UINT64 offset = ((FB_UINT64) page_number) * ((FB_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);
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);
const ULONG page_size = rbdb->rbdb_page_size;
int fd = rbdb->rbdb_file.fil_file;
2001-05-23 15:26:42 +02:00
2007-11-17 01:38:16 +01:00
const FB_UINT64 offset = ((FB_UINT64) page_number) * ((FB_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];
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;
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.
*
**************************************/
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;
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++;
} 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.
*
**************************************/
ULONG sequence = 0;
2001-05-23 15:26:42 +02:00
if (rbdb->rbdb_last_page && upper == BIG_NUMBER)
upper = rbdb->rbdb_last_page;
PAG page;
2001-05-23 15:26:42 +02:00
while (page = RBDB_read(rbdb, lower)) {
if (page->pag_type == pag_transactions && tips) {
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);
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
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++)
{
2001-05-23 15:26:42 +02:00
DMP_fetched_page(page, *tip++, sequence, rbdb->rbdb_page_size);
}
2001-05-23 15:26:42 +02:00
}
static void format_header(
RBDB rbdb,
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;
page->pag_type = pag_header;
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;
page->pag_checksum = compute_checksum(rbdb, page);
2001-05-23 15:26:42 +02:00
}
static void format_index_root(
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);
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(
pointer_page* page,
2001-05-23 15:26:42 +02:00
int page_size,
SSHORT relation_id,
SSHORT sequence,
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);
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)
page->pag_flags |= ppg_eof;
2001-05-23 15:26:42 +02:00
memcpy(page->ppg_page, page_vector, count * sizeof(SLONG));
}
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.
*
**************************************/
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 */
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;
}
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.
*
**************************************/
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 */
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);
}
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.
*
**************************************/
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])
{
if (*p == HDR_file)
{
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;
}
}
2001-05-23 15:26:42 +02:00
}
static void get_range(
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;
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;
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;
2006-04-06 10:18:53 +02:00
if ((*token->swc_string == ':') || (*token->swc_string == ','))
{
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++;
}
}
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;
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
}
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;
header_page* header = (header_page*) RBDB_read(rbdb, (SLONG) 0);
2001-05-23 15:26:42 +02: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",
pag_header, header->pag_type);
2001-05-23 15:26:42 +02:00
rbdb->rbdb_valid = FALSE;
}
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);
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;
isc_decode_timestamp(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;
/*
2001-05-23 15:26:42 +02:00
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;
*/
2001-05-23 15:26:42 +02:00
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;
/*
2001-05-23 15:26:42 +02:00
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;
*/
2001-05-23 15:26:42 +02:00
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;
/*
2001-05-23 15:26:42 +02:00
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;
*/
2001-05-23 15:26:42 +02:00
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;
/*
2001-05-23 15:26:42 +02:00
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;
*/
2001-05-23 15:26:42 +02:00
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;
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++)
{
pointer_page* pointer = (pointer_page*) page;
/* 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);
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);
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",
((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);
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-12-16 04:03:13 +01:00
(pointer->pag_flags & ppg_eof) ?
"last pointer for relation\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_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);
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-12-16 04:03:13 +01:00
(data->pag_flags & dpg_orphan) ? "orphan " : "",
(data->pag_flags & dpg_full) ? "full " : "",
2004-12-16 04:03:13 +01:00
(data->pag_flags & dpg_large) ?
"contains a large object" : "",
(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);
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);
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-12-16 04:03:13 +01:00
(bucket->pag_flags & btr_leftmost) ? "leftmost " : "",
(bucket->pag_flags & btr_not_prop) ?
"all duplicates " : "",
(bucket->Pag_flags & btr_marked) ?
"marked for delete" : "");
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_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);
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-12-16 04:03:13 +01:00
(blob->pag_flags & blp_pointers) ? "pointers" : "data");
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_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