From 7bb481fda9ecb134804b49c2ce77ca28f7eea583 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Fri, 30 Mar 2012 20:42:12 -0400 Subject: Imported Upstream version 2.0.3 --- tool/restore_jrnl.tcl | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 tool/restore_jrnl.tcl (limited to 'tool/restore_jrnl.tcl') diff --git a/tool/restore_jrnl.tcl b/tool/restore_jrnl.tcl new file mode 100644 index 0000000..05af4f9 --- /dev/null +++ b/tool/restore_jrnl.tcl @@ -0,0 +1,233 @@ +# 2010 January 7 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements utility functions for SQLite library. +# +# This file attempts to restore the header of a journal. +# This may be useful for rolling-back the last committed +# transaction from a recovered journal. +# + +package require sqlite3 + +set parm_error 0 +set fix_chksums 0 +set dump_pages 0 +set db_name "" + +for {set i 0} {$i<$argc} {incr i} { + if {[lindex $argv $i] == "-fix_chksums"} { + set fix_chksums -1 + } elseif {[lindex $argv $i] == "-dump_pages"} { + set dump_pages -1 + } elseif {$db_name == ""} { + set db_name [lindex $argv $i] + set jrnl_name $db_name-journal + } else { + set parm_error -1 + } +} +if {$parm_error || $db_name == ""} { + puts "USAGE: restore_jrnl.tcl \[-fix_chksums\] \[-dump_pages\] db_name" + puts "Example: restore_jrnl.tcl foo.sqlite" + return +} + +# is there a way to determine this? +set sectsz 512 + +# Copy file $from into $to +# +proc copy_file {from to} { + file copy -force $from $to +} + +# Execute some SQL +# +proc catchsql {sql} { + set rc [catch {uplevel [list db eval $sql]} msg] + list $rc $msg +} + +# Perform a test +# +proc do_test {name cmd expected} { + puts -nonewline "$name ..." + set res [uplevel $cmd] + if {$res eq $expected} { + puts Ok + } else { + puts Error + puts " Got: $res" + puts " Expected: $expected" + } +} + +# Calc checksum nonce from journal page data. +# +proc calc_nonce {jrnl_pgno} { + global sectsz + global db_pgsz + global jrnl_name + set jrnl_pg_offset [expr $sectsz+((4+$db_pgsz+4)*$jrnl_pgno)] + set nonce [hexio_get_int [hexio_read $jrnl_name [expr $jrnl_pg_offset+4+$db_pgsz] 4]] + for {set i [expr $db_pgsz-200]} {$i>0} {set i [expr $i-200]} { + set byte [hexio_get_int [hexio_read $jrnl_name [expr $jrnl_pg_offset+4+$i] 1]] + set nonce [expr $nonce-$byte] + } + return $nonce +} + +# Calc checksum from journal page data. +# +proc calc_chksum {jrnl_pgno} { + global sectsz + global db_pgsz + global jrnl_name + global nonce + set jrnl_pg_offset [expr $sectsz+((4+$db_pgsz+4)*$jrnl_pgno)] + set chksum $nonce + for {set i [expr $db_pgsz-200]} {$i>0} {set i [expr $i-200]} { + set byte [hexio_get_int [hexio_read $jrnl_name [expr $jrnl_pg_offset+4+$i] 1]] + set chksum [expr $chksum+$byte] + } + return $chksum +} + +# Print journal page data in hex dump form +# +proc dump_jrnl_page {jrnl_pgno} { + global sectsz + global db_pgsz + global jrnl_name + + # print a header block for the page + puts [string repeat "-" 79] + set jrnl_pg_offset [expr $sectsz+((4+$db_pgsz+4)*$jrnl_pgno)] + set db_pgno [hexio_get_int [hexio_read $jrnl_name [expr $jrnl_pg_offset] 4]] + set chksum [hexio_get_int [hexio_read $jrnl_name [expr $jrnl_pg_offset+4+$db_pgsz] 4]] + set nonce [calc_nonce $jrnl_pgno] + puts [ format {jrnl_pg_offset: %08x (%d) jrnl_pgno: %d db_pgno: %d} \ + $jrnl_pg_offset $jrnl_pg_offset \ + $jrnl_pgno $db_pgno] + puts [ format {nonce: %08x chksum: %08x} \ + $nonce $chksum] + + # now hex dump the data + # This is derived from the Tcler's WIKI + set fid [open $jrnl_name r] + fconfigure $fid -translation binary -encoding binary + seek $fid [expr $jrnl_pg_offset+4] + set data [read $fid $db_pgsz] + close $fid + for {set addr 0} {$addr<$db_pgsz} {set addr [expr $addr+16]} { + # get 16 bytes of data + set s [string range $data $addr [expr $addr+16]] + + # Convert the data to hex and to characters. + binary scan $s H*@0a* hex ascii + + # Replace non-printing characters in the data. + regsub -all -- {[^[:graph:] ]} $ascii {.} ascii + + # Split the 16 bytes into two 8-byte chunks + regexp -- {(.{16})(.{0,16})} $hex -> hex1 hex2 + + # Convert the hex to pairs of hex digits + regsub -all -- {..} $hex1 {& } hex1 + regsub -all -- {..} $hex2 {& } hex2 + + # Print the hex and ascii data + puts [ format {%08x %-24s %-24s %-16s} \ + $addr $hex1 $hex2 $ascii ] + } +} + +# Setup for the tests. Make a backup copy of the files. +# +if [file exist $db_name.org] { + puts "ERROR: during back-up: $db_name.org exists already." + return; +} +if [file exist $jrnl_name.org] { + puts "ERROR: during back-up: $jrnl_name.org exists already." + return +} +copy_file $db_name $db_name.org +copy_file $jrnl_name $jrnl_name.org + +set db_fsize [file size $db_name] +set db_pgsz [hexio_get_int [hexio_read $db_name 16 2]] +set db_npage [expr {$db_fsize / $db_pgsz}] + +set jrnl_fsize [file size $jrnl_name] +set jrnl_npage [expr {($jrnl_fsize - $sectsz) / (4 + $db_pgsz + 4)}] + +# calculate checksum nonce for first page +set nonce [calc_nonce 0] + +# verify all the pages in the journal use the same nonce +for {set i 1} {$i<$jrnl_npage} {incr i} { + set tnonce [calc_nonce $i] + if {$tnonce != $nonce} { + puts "WARNING: different nonces: 0=$nonce $i=$tnonce" + if {$fix_chksums } { + set jrnl_pg_offset [expr $sectsz+((4+$db_pgsz+4)*$i)] + set tchksum [calc_chksum $i] + hexio_write $jrnl_name [expr $jrnl_pg_offset+4+$db_pgsz] [format %08x $tchksum] + puts "INFO: fixing chksum: $i=$tchksum" + } + } +} + +# verify all the page numbers in the journal +for {set i 0} {$i<$jrnl_npage} {incr i} { + set jrnl_pg_offset [expr $sectsz+((4+$db_pgsz+4)*$i)] + set db_pgno [hexio_get_int [hexio_read $jrnl_name $jrnl_pg_offset 4]] + if {$db_pgno < 1} { + puts "WARNING: page number < 1: $i=$db_pgno" + } + if {$db_pgno >= $db_npage} { + puts "WARNING: page number >= $db_npage: $i=$db_pgno" + } +} + +# dump page data +if {$dump_pages} { + for {set i 0} {$i<$jrnl_npage} {incr i} { + dump_jrnl_page $i + } +} + +# write the 8 byte magic string +hexio_write $jrnl_name 0 d9d505f920a163d7 + +# write -1 for number of records +hexio_write $jrnl_name 8 ffffffff + +# write 00 for checksum nonce +hexio_write $jrnl_name 12 [format %08x $nonce] + +# write page count +hexio_write $jrnl_name 16 [format %08x $db_npage] + +# write sector size +hexio_write $jrnl_name 20 [format %08x $sectsz] + +# write page size +hexio_write $jrnl_name 24 [format %08x $db_pgsz] + +# check the integrity of the database with the patched journal +sqlite3 db $db_name +do_test restore_jrnl-1.0 { + catchsql {PRAGMA integrity_check} +} {0 ok} +db close + -- cgit v1.2.3