// Program: GLINK // Author: Ivan Prenosil #define VERSION "v0.4" #define PROGNAME "glink" #include #include #include #include #include #include #include #define MAX_FILENAME 300 #define MAX_IBFILENAME 253 #define MIN_PAGE_SIZE 1024 #define MAX_PAGE_SIZE 32768 // variables for storing parsed command line char prog_name [MAX_FILENAME + 1]; char db_name [MAX_FILENAME + 1]; char db_link_name [MAX_IBFILENAME + 1]; char orig_link_name [257]; // clumpets have 1 byte length int sw_show_header = 0; int sw_show_header_full = 0; int sw_show_help = 0; int sw_show_version = 0; // working global variables FILE *db = NULL; #define EXIT_OK close_db_file(); exit(0); #define EXIT_WARNING close_db_file(); exit(1); #define EXIT_FATAL close_db_file(); exit(2); typedef signed short SSHORT; typedef unsigned short USHORT; typedef signed long SLONG; typedef unsigned long ULONG; typedef char SCHAR; typedef unsigned char UCHAR; /* Basic page header */ typedef struct pag { SCHAR pag_type; SCHAR pag_flags; USHORT pag_checksum; ULONG pag_generation; ULONG pag_seqno; /* WAL seqno of last update */ ULONG pag_offset; /* WAL offset of last update */ }; /* Header page */ typedef struct hdr { struct pag hdr_header; USHORT hdr_page_size; /* Page size of database */ USHORT hdr_ods_version; /* Version of on-disk structure */ SLONG hdr_PAGES; /* Page number of PAGES relation */ ULONG hdr_next_page; /* Page number of next hdr page */ SLONG hdr_oldest_transaction; /* Oldest interesting transaction */ SLONG hdr_oldest_active; /* Oldest transaction thought active */ SLONG hdr_next_transaction; /* Next transaction id */ USHORT hdr_sequence; /* sequence number of file */ USHORT hdr_flags; /* Flag settings, see below */ SLONG hdr_creation_date [2]; /* Date/time of creation */ SLONG hdr_attachment_id; /* Next attachment id */ SLONG hdr_shadow_count; /* Event count for shadow synchronization */ SSHORT hdr_implementation; /* Implementation number */ USHORT hdr_ods_minor; /* Update version of ODS */ USHORT hdr_ods_minor_original; /* Update version of ODS at creation */ USHORT hdr_end; /* offset of HDR_end in page */ ULONG hdr_page_buffers; /* Page buffers for database cache */ SLONG hdr_bumped_transaction; /* Bumped transaction id for log optimization */ SLONG hdr_oldest_snapshot; /* Oldest snapshot of active transactions */ SLONG hdr_misc [4]; /* Stuff to be named later */ // UCHAR hdr_data [1]; /* Misc data */ }; typedef struct hdr_page { struct hdr fix_data; UCHAR var_data[MAX_PAGE_SIZE-sizeof(struct hdr)]; }; struct hdr_page header_page, new_header_page; void parse_args (int argc, char *argv[]); void show_error (char *str); void show_error_str (char *str, char *val); void show_error_int (char *str, int); void show_version (void); void show_help (void); void show_db_header(void); void show_link_name (void); USHORT compute_checksum (struct hdr_page *hdr, USHORT pagesize); void open_db_file(int exclusive); void close_db_file(void); void get_db_header(void); void check_db_header(void); void update_db_header(void); /************************* * * M A I N * *************************/ void main (int argc, char *argv[]) { if (argc<=1) { sw_show_help = 1; printf ("Copyright(C)2001 Ivan Prenosil\n"); printf ("This program is intended to simplify moving multifile databases.\n"); printf ("It changes name of next file in chain stored in database header\n"); printf ("(by directly modifying header page of IB/FB database).\n"); printf ("It does not rename/move the file itself.\n"); printf ("!!! Use at your own risk !!!\n\n"); } parse_args (argc, argv); if (sw_show_version) show_version (); if (sw_show_help) show_help (); if (db_name[0]) { open_db_file (db_link_name[0]); get_db_header (); if (sw_show_header) show_db_header (); check_db_header (); if (db_link_name[0]) update_db_header (); else { if (!sw_show_header) show_link_name (); } close_db_file (); } else { if (sw_show_header) { show_error ("Database file not specified."); EXIT_FATAL; } } EXIT_OK; } /**************************************** * * p a r s e _ a r g s * ****************************************/ void parse_args (int argc, char *argv[]) { register int i; char tmp[300]; int param_number = 0; // last assigned un-specified parameter (i.e. without -something switch) db_name [0] = '\0'; db_link_name [0] = '\0'; orig_link_name [0] = '\0'; #ifdef PROGNAME strcpy (prog_name, PROGNAME); #else strncpy (prog_name, argv[0], MAX_FILENAME); prog_name[MAX_FILENAME] = '\0'; #endif fnsplit (argv[0], tmp, tmp, prog_name, tmp); for (i = 1; i < argc; i++) if (argv[i][0] != '-') { switch (param_number++) { case 0: { if (strlen (argv[i]) > MAX_FILENAME) { show_error_str ("Max. length of db file exceeded.", argv[i]); EXIT_FATAL; } strncpy (db_name, argv[i], MAX_FILENAME); db_name [MAX_FILENAME] = '\0'; break; } case 1: { if (strlen (argv[i]) > MAX_IBFILENAME) { show_error_str ("Max. length of linked db file exceeded.", argv[i]); EXIT_FATAL; } strncpy (db_link_name, argv[i], MAX_IBFILENAME); db_link_name [MAX_IBFILENAME] = '\0'; break; } default: { show_error_str ("Unexpected parameter.", argv[i]); show_help (); EXIT_FATAL; } } //switch } //if else { switch (argv[i][1]) { case 'H' : sw_show_header_full = 1; case 'h' : /* show header */ sw_show_header = 1; break; case '?' : /* help switch */ sw_show_help = 1; break; case 'v' : /* program version */ case 'V' : case 'z' : /* program version */ case 'Z' : sw_show_version = 1; break; default: show_error_str ("Invalid switch!", argv[i]); show_help (); EXIT_FATAL; } // switch } //if-else } /************************************ * * s h o w _ e r r o r * ************************************/ void show_error (char *str) { fprintf (stderr, "ERROR: %s\n\n",str); if (errno) perror(""); } /************************************** * * s h o w _ e r r o r _ str * **************************************/ void show_error_str (char *str, char *val) { fprintf (stderr, "ERROR: %s (%s)\n\n", str, val); if (errno) perror(""); } /************************************** * * s h o w _ e r r o r _ i n t * **************************************/ void show_error_int (char *str, int val) { fprintf (stderr, "ERROR: %s (%d)\n\n", str, val); if (errno) perror(""); } /**************************************** * * s h o w _ v e r s i o n * ****************************************/ void show_version (void) { printf ("Version: %s\n\n", VERSION); } /********************************** * * s h o w _ h e l p * **********************************/ void show_help (void) { printf ("Syntax: %s [switches] [db_file_name [new_name]]\n",prog_name); printf ("\t-h: print header\n"); printf ("\t-?: print this help text\n"); printf ("\t-z: print version information\n"); printf ("\n%s \n will show name of next file in chain.\n",prog_name); printf ("\n%s \n will change name of next file.\n",prog_name); printf ("\n"); } /****************************************** * * s h o w _ d b _ h e a d e r * ******************************************/ void show_db_header (void) { char* str; int i, j, k, len; struct tm *t; time_t ti; char buf[80]; if (0 < header_page.fix_data.hdr_sequence) { printf ("\nDatabase header page information:\n"); printf ("\tSequence number\t\t%d\n", header_page.fix_data.hdr_sequence); } else { printf ("\nDatabase header page information:\n"); if (sw_show_header_full) { printf ("\tPage type\t\t%d\n", header_page.fix_data.hdr_header.pag_type); printf ("\tFlags\t\t\t%d\n", header_page.fix_data.hdr_header.pag_flags); printf ("\tChecksum\t\t%d\n", header_page.fix_data.hdr_header.pag_checksum); } printf ("\tGeneration\t\t%d\n", header_page.fix_data.hdr_header.pag_generation); printf ("\tPage size\t\t%d\n", header_page.fix_data.hdr_page_size); printf ("\tODS version\t\t%d.%d\n", header_page.fix_data.hdr_ods_version, header_page.fix_data.hdr_ods_minor); printf ("\tOldest transaction\t%ld\n", header_page.fix_data.hdr_oldest_transaction); printf ("\tOldest active\t\t%ld\n", header_page.fix_data.hdr_oldest_active); printf ("\tOldest snapshot\t\t%ld\n", header_page.fix_data.hdr_oldest_snapshot); printf ("\tNext transaction\t%ld\n", header_page.fix_data.hdr_next_transaction); if (sw_show_header_full) { printf ("\tBumped transaction\t%ld\n", header_page.fix_data.hdr_bumped_transaction); } printf ("\tSequence number\t\t%d\n", header_page.fix_data.hdr_sequence); if (sw_show_header_full) { printf ("\tNext attachment ID\t%ld\n", header_page.fix_data.hdr_attachment_id); } switch (header_page.fix_data.hdr_implementation) { case 1: str = "HP Apollo DomainOS"; break; case 2: str = "Sun Solaris SPARC, HP9000 s300, Xenix, Motorola IMP UNIX, UnixWare, NCR UNIX, NeXT, Data General DG-UX Intel"; break; case 3: str = "Sun Solaris x86"; break; case 4: str = "VMS"; break; case 5: str = "Ultrix VAX"; break; case 6: str = "Ultrix MIPS"; break; case 7: str = "HP9000 s700/s800"; break; case 8: str = "Novell NetWare"; break; case 9: str = "Apple Macintosh 680x0"; break; case 10: str = "IBM AIX POWER series, IBM AIX PowerPC"; break; case 11: str = "Data General DG-UX 88K"; break; case 12: str = "MPE/xl"; break; case 13: str = "SGI IRIX"; break; case 14: str = "Cray"; break; case 15: str = "OSF/1"; break; case 16: str = "Microsoft Windows 32-bit Intel"; break; case 17: str = "IBM OS/2"; break; case 18: str = "Microsoft Windows 16-bit"; break; case 19: str = "Linux Intel"; break; case 20: str = "Linux SPARC"; break; default: str = ""; break; } printf ("\tImplementation ID\t%d (%s)\n", header_page.fix_data.hdr_implementation, str); printf ("\tShadow count\t\t%ld\n", header_page.fix_data.hdr_shadow_count); printf ("\tPage buffers\t\t%d\n", header_page.fix_data.hdr_page_buffers); if (sw_show_header_full) { printf ("\tNext header page\t%d\n", header_page.fix_data.hdr_next_page); } if (header_page.fix_data.hdr_ods_version >= 10) { if (0x100 & header_page.fix_data.hdr_flags) printf ("\tDatabase dialect\t3\n"); else printf ("\tDatabase dialect\t1\n"); } ti = 86400 * (header_page.fix_data.hdr_creation_date[0] - 40587) + _timezone + (header_page.fix_data.hdr_creation_date[1] / 10000); if (_daylight) ti = ti - 3600; t = localtime (&ti); if (strftime (buf, sizeof(buf)-1, "%b %d, %Y %X", t)) printf ("\tCreation date\t\t%s\n", buf); } if (header_page.fix_data.hdr_flags) { printf ("Attributes:\n"); if (0x0001 & header_page.fix_data.hdr_flags) printf ("\tactive shadow\n"); if (0x0002 & header_page.fix_data.hdr_flags) printf ("\tforce write\n"); if (0x0020 & header_page.fix_data.hdr_flags) printf ("\tno reserve\n"); if (0x0040 & header_page.fix_data.hdr_flags) printf ("\tshared cache disabled\n"); if (0x0080 & header_page.fix_data.hdr_flags) printf ("\tdatabase shutdown\n"); if (0x0200 & header_page.fix_data.hdr_flags) printf ("\tread only\n"); } // part common for both first file and continuation files printf ("\nVariable header data:\n"); i = 0; while (header_page.var_data[i]) { switch (header_page.var_data[i]) { case 1: str = "\tRoot file name: %.*s\n"; break; case 2: str = "\tJournal server: %.*s\n"; break; case 3: str = "\tContinuation file: %.*s\n"; break; case 7: str = "\tReplay logging file: %.*s\n"; break; case 11: str = "\tShared Cache file: %.*s\n"; break; case 4: str = "\tLast logical page: %ld\n"; break; case 5: str = "\tUnlicensed accesses: %ld\n"; break; case 6: str = "\tSweep interval: %ld\n"; break; default: str = "\tUnrecognized option %d, length %d\n"; break; // "\tEncoded option %d, length %d\n" } len = header_page.var_data[i+1]; if (((int) sizeof(header_page.fix_data) + i + len + 2) > header_page.fix_data.hdr_page_size ) { show_error ("Variable header data exceed page length."); EXIT_FATAL; } switch (header_page.var_data[i]) { case 1: case 2: case 3: case 7: case 11: printf (str, len, &header_page.var_data[i+2]); break; case 4: case 5: case 6: if (len > 4) { show_error_int ("Variable header integer data longer than 4 bytes.", len); EXIT_FATAL; } for (j = 0, k = len; k--; j = (j << 8) + header_page.var_data[i+k+2]); printf (str, j); break; default: printf (str, header_page.var_data[i], len); break; } i += len + 2; } printf ("\t*END*\n"); printf ("\n"); } /****************************************** * * s h o w _ l i n k _ n a m e * ******************************************/ void show_link_name (void) { int i, len, found; found = 0; i = 0; while (header_page.var_data[i]) { len = header_page.var_data[i+1]; if (((int) sizeof(header_page.fix_data) + i + len + 2) > header_page.fix_data.hdr_page_size ) { show_error ("Variable header data exceed page length."); EXIT_FATAL; } if (3 == header_page.var_data[i]) { memcpy (orig_link_name, &header_page.var_data[i+2], len); orig_link_name[len] = 0; printf ("Continuation file:\t\"%s\"\n\n", orig_link_name); found = 1; break; } i += len + 2; } if (! found ) { printf ("This is last file (Continuation file name not found in header)\n\n"); EXIT_OK; } } /************************************************ * * c o m p u t e _ c h e c k s u m * ************************************************/ USHORT compute_checksum (struct hdr_page *hdr, USHORT pagesize) { ULONG *p; ULONG checksum = 0; USHORT old_checksum; if (pagesize != 1024 && pagesize != 2048 && pagesize != 4096 && pagesize != 8192 && pagesize != 16384 && pagesize != 32768) return (hdr->fix_data.hdr_header.pag_checksum); // for wrong page size do not compute anything old_checksum = hdr->fix_data.hdr_header.pag_checksum; hdr->fix_data.hdr_header.pag_checksum = 0; p = (ULONG *)hdr; pagesize = pagesize / sizeof(*p); while (pagesize--) { checksum += *p++; } hdr->fix_data.hdr_header.pag_checksum = old_checksum; return (checksum); } /**************************************** * * o p e n _ d b _ f i l e * ****************************************/ void open_db_file(int exclusive) { int i; if (exclusive) { if ( -1 == (i= _sopen (db_name, O_RDWR | O_BINARY, SH_DENYWR, 0)) ) { show_error ("Cannot open database file."); EXIT_FATAL; } db = _fdopen (i, "r+b"); } else { if ( NULL == (db = fopen (db_name, "rb")) ) { show_error ("Cannot open database file for reading."); EXIT_FATAL; } } } /**************************************** * * c l o s e _ d b _ f i l e * ****************************************/ void close_db_file(void) { if (db) fclose (db); } /**************************************** * * g e t _ d b _ h e a d e r * ****************************************/ void get_db_header(void) { if ( 1 != fread (&header_page, MIN_PAGE_SIZE, 1, db) ) { show_error ("Cannot read header page."); EXIT_FATAL; } switch (header_page.fix_data.hdr_page_size) { case 1024: break; case 2048: case 4096: case 8192: case 16384: case 32768: // read header page in full length rewind (db); if ( 1 != fread (&header_page, header_page.fix_data.hdr_page_size, 1, db) ) { show_error ("Cannot read header page."); EXIT_FATAL; } break; default: show_error_int ("Wrong database page size.", header_page.fix_data.hdr_page_size); EXIT_FATAL; } } /**************************************** * * c h e c k _ d b _ h e a d e r * ****************************************/ void check_db_header(void) { int is_win32; USHORT required_checksum; if (1 != header_page.fix_data.hdr_header.pag_type) { show_error_int ("Wrong header page type, expected 1 found", header_page.fix_data.hdr_header.pag_type); EXIT_FATAL; } if (header_page.fix_data.hdr_next_page) { /* Page number of next hdr page */ show_error ("This program does not support continuation header pages."); EXIT_FATAL; } // 16 = "Microsoft Windows 32-bit Intel"; // 18 = "Microsoft Windows 16-bit"; is_win32 = (16 == header_page.fix_data.hdr_implementation); // IB4 = ODS 8; IB5 = ODS 9; IB6 = ODS 10 // also see jrd/cch.c (CCH_checksum()) if (((!is_win32) && (header_page.fix_data.hdr_ods_version >= 8)) || (( is_win32) && (header_page.fix_data.hdr_ods_version >= 9)) ) { required_checksum = 12345; } else { required_checksum = compute_checksum (&header_page, header_page.fix_data.hdr_page_size); } if (required_checksum != header_page.fix_data.hdr_header.pag_checksum) { printf ("WARNING: Checksum error, expected %d, found %d.\n\n", required_checksum, header_page.fix_data.hdr_header.pag_checksum); } } /********************************************* * * u p d a t e _ d b _ h e a d e r * *********************************************/ void update_db_header(void) { int src_i, dst_i, len, found; int is_win32; memset (&new_header_page, 0, sizeof(new_header_page)); new_header_page.fix_data = header_page.fix_data; found = 0; src_i = 0; dst_i = 0; while (header_page.var_data[src_i]) { len = header_page.var_data[src_i+1]; if (3 == header_page.var_data[src_i]) { if (((int)sizeof (new_header_page.fix_data) + dst_i + (int)strlen (db_link_name) + 2) > new_header_page.fix_data.hdr_page_size ) { show_error ("New file name does not fit into header page."); EXIT_FATAL; } memcpy (orig_link_name, &header_page.var_data[src_i+2], len); orig_link_name[len] = 0; printf ("Link changed from\n \"%s\"\nto\n \"%s\"\n\n", orig_link_name, db_link_name); new_header_page.var_data[dst_i++] = 3; new_header_page.var_data[dst_i++] = strlen (db_link_name); strcpy (&new_header_page.var_data[dst_i], db_link_name); dst_i += strlen (db_link_name); found = 1; } else { if (((int)sizeof (new_header_page.fix_data) + dst_i + len + 2) > new_header_page.fix_data.hdr_page_size ) { show_error ("Variable header data too long."); EXIT_FATAL; } if (((int)sizeof (header_page.fix_data) + src_i + len + 2) > header_page.fix_data.hdr_page_size ) { show_error ("Variable header data error."); EXIT_FATAL; } memcpy (&new_header_page.var_data[dst_i], &header_page.var_data[src_i], len+2); dst_i += len + 2; } src_i += len + 2; } if (! found ) { show_error ("Continuation file not specified in header."); EXIT_FATAL; } // compute new checksum // 16 = "Microsoft Windows 32-bit Intel"; // 18 = "Microsoft Windows 16-bit"; is_win32 = (16 == new_header_page.fix_data.hdr_implementation); // IB4 = ODS 8; IB5 = ODS 9; IB6 = ODS 10 // also see jrd/cch.c (CCH_checksum()) if (((!is_win32) && (new_header_page.fix_data.hdr_ods_version >= 8)) || (( is_win32) && (new_header_page.fix_data.hdr_ods_version >= 9)) ) { new_header_page.fix_data.hdr_header.pag_checksum = 12345; } else { new_header_page.fix_data.hdr_header.pag_checksum = compute_checksum (&new_header_page, new_header_page.fix_data.hdr_page_size); } // write new version of header rewind (db); if ( 1 != fwrite (&new_header_page, new_header_page.fix_data.hdr_page_size, 1, db) ) show_error ("Cannot write header page."); }