summaryrefslogtreecommitdiff
path: root/tool/restore_jrnl.tcl
diff options
context:
space:
mode:
authorHans-Christoph Steiner <hans@eds.org>2012-03-30 20:42:12 -0400
committerHans-Christoph Steiner <hans@eds.org>2012-03-30 20:42:12 -0400
commit7bb481fda9ecb134804b49c2ce77ca28f7eea583 (patch)
tree31b520b9914d3e2453968abe375f2c102772c3dc /tool/restore_jrnl.tcl
Imported Upstream version 2.0.3
Diffstat (limited to 'tool/restore_jrnl.tcl')
-rw-r--r--tool/restore_jrnl.tcl233
1 files changed, 233 insertions, 0 deletions
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
+