Annotation of embedaddon/sqlite3/tool/restore_jrnl.tcl, revision 1.1
1.1 ! misho 1: # 2010 January 7
! 2: #
! 3: # The author disclaims copyright to this source code. In place of
! 4: # a legal notice, here is a blessing:
! 5: #
! 6: # May you do good and not evil.
! 7: # May you find forgiveness for yourself and forgive others.
! 8: # May you share freely, never taking more than you give.
! 9: #
! 10: #***********************************************************************
! 11: # This file implements utility functions for SQLite library.
! 12: #
! 13: # This file attempts to restore the header of a journal.
! 14: # This may be useful for rolling-back the last committed
! 15: # transaction from a recovered journal.
! 16: #
! 17:
! 18: package require sqlite3
! 19:
! 20: set parm_error 0
! 21: set fix_chksums 0
! 22: set dump_pages 0
! 23: set db_name ""
! 24:
! 25: for {set i 0} {$i<$argc} {incr i} {
! 26: if {[lindex $argv $i] == "-fix_chksums"} {
! 27: set fix_chksums -1
! 28: } elseif {[lindex $argv $i] == "-dump_pages"} {
! 29: set dump_pages -1
! 30: } elseif {$db_name == ""} {
! 31: set db_name [lindex $argv $i]
! 32: set jrnl_name $db_name-journal
! 33: } else {
! 34: set parm_error -1
! 35: }
! 36: }
! 37: if {$parm_error || $db_name == ""} {
! 38: puts "USAGE: restore_jrnl.tcl \[-fix_chksums\] \[-dump_pages\] db_name"
! 39: puts "Example: restore_jrnl.tcl foo.sqlite"
! 40: return
! 41: }
! 42:
! 43: # is there a way to determine this?
! 44: set sectsz 512
! 45:
! 46: # Copy file $from into $to
! 47: #
! 48: proc copy_file {from to} {
! 49: file copy -force $from $to
! 50: }
! 51:
! 52: # Execute some SQL
! 53: #
! 54: proc catchsql {sql} {
! 55: set rc [catch {uplevel [list db eval $sql]} msg]
! 56: list $rc $msg
! 57: }
! 58:
! 59: # Perform a test
! 60: #
! 61: proc do_test {name cmd expected} {
! 62: puts -nonewline "$name ..."
! 63: set res [uplevel $cmd]
! 64: if {$res eq $expected} {
! 65: puts Ok
! 66: } else {
! 67: puts Error
! 68: puts " Got: $res"
! 69: puts " Expected: $expected"
! 70: }
! 71: }
! 72:
! 73: # Calc checksum nonce from journal page data.
! 74: #
! 75: proc calc_nonce {jrnl_pgno} {
! 76: global sectsz
! 77: global db_pgsz
! 78: global jrnl_name
! 79: set jrnl_pg_offset [expr $sectsz+((4+$db_pgsz+4)*$jrnl_pgno)]
! 80: set nonce [hexio_get_int [hexio_read $jrnl_name [expr $jrnl_pg_offset+4+$db_pgsz] 4]]
! 81: for {set i [expr $db_pgsz-200]} {$i>0} {set i [expr $i-200]} {
! 82: set byte [hexio_get_int [hexio_read $jrnl_name [expr $jrnl_pg_offset+4+$i] 1]]
! 83: set nonce [expr $nonce-$byte]
! 84: }
! 85: return $nonce
! 86: }
! 87:
! 88: # Calc checksum from journal page data.
! 89: #
! 90: proc calc_chksum {jrnl_pgno} {
! 91: global sectsz
! 92: global db_pgsz
! 93: global jrnl_name
! 94: global nonce
! 95: set jrnl_pg_offset [expr $sectsz+((4+$db_pgsz+4)*$jrnl_pgno)]
! 96: set chksum $nonce
! 97: for {set i [expr $db_pgsz-200]} {$i>0} {set i [expr $i-200]} {
! 98: set byte [hexio_get_int [hexio_read $jrnl_name [expr $jrnl_pg_offset+4+$i] 1]]
! 99: set chksum [expr $chksum+$byte]
! 100: }
! 101: return $chksum
! 102: }
! 103:
! 104: # Print journal page data in hex dump form
! 105: #
! 106: proc dump_jrnl_page {jrnl_pgno} {
! 107: global sectsz
! 108: global db_pgsz
! 109: global jrnl_name
! 110:
! 111: # print a header block for the page
! 112: puts [string repeat "-" 79]
! 113: set jrnl_pg_offset [expr $sectsz+((4+$db_pgsz+4)*$jrnl_pgno)]
! 114: set db_pgno [hexio_get_int [hexio_read $jrnl_name [expr $jrnl_pg_offset] 4]]
! 115: set chksum [hexio_get_int [hexio_read $jrnl_name [expr $jrnl_pg_offset+4+$db_pgsz] 4]]
! 116: set nonce [calc_nonce $jrnl_pgno]
! 117: puts [ format {jrnl_pg_offset: %08x (%d) jrnl_pgno: %d db_pgno: %d} \
! 118: $jrnl_pg_offset $jrnl_pg_offset \
! 119: $jrnl_pgno $db_pgno]
! 120: puts [ format {nonce: %08x chksum: %08x} \
! 121: $nonce $chksum]
! 122:
! 123: # now hex dump the data
! 124: # This is derived from the Tcler's WIKI
! 125: set fid [open $jrnl_name r]
! 126: fconfigure $fid -translation binary -encoding binary
! 127: seek $fid [expr $jrnl_pg_offset+4]
! 128: set data [read $fid $db_pgsz]
! 129: close $fid
! 130: for {set addr 0} {$addr<$db_pgsz} {set addr [expr $addr+16]} {
! 131: # get 16 bytes of data
! 132: set s [string range $data $addr [expr $addr+16]]
! 133:
! 134: # Convert the data to hex and to characters.
! 135: binary scan $s H*@0a* hex ascii
! 136:
! 137: # Replace non-printing characters in the data.
! 138: regsub -all -- {[^[:graph:] ]} $ascii {.} ascii
! 139:
! 140: # Split the 16 bytes into two 8-byte chunks
! 141: regexp -- {(.{16})(.{0,16})} $hex -> hex1 hex2
! 142:
! 143: # Convert the hex to pairs of hex digits
! 144: regsub -all -- {..} $hex1 {& } hex1
! 145: regsub -all -- {..} $hex2 {& } hex2
! 146:
! 147: # Print the hex and ascii data
! 148: puts [ format {%08x %-24s %-24s %-16s} \
! 149: $addr $hex1 $hex2 $ascii ]
! 150: }
! 151: }
! 152:
! 153: # Setup for the tests. Make a backup copy of the files.
! 154: #
! 155: if [file exist $db_name.org] {
! 156: puts "ERROR: during back-up: $db_name.org exists already."
! 157: return;
! 158: }
! 159: if [file exist $jrnl_name.org] {
! 160: puts "ERROR: during back-up: $jrnl_name.org exists already."
! 161: return
! 162: }
! 163: copy_file $db_name $db_name.org
! 164: copy_file $jrnl_name $jrnl_name.org
! 165:
! 166: set db_fsize [file size $db_name]
! 167: set db_pgsz [hexio_get_int [hexio_read $db_name 16 2]]
! 168: set db_npage [expr {$db_fsize / $db_pgsz}]
! 169:
! 170: set jrnl_fsize [file size $jrnl_name]
! 171: set jrnl_npage [expr {($jrnl_fsize - $sectsz) / (4 + $db_pgsz + 4)}]
! 172:
! 173: # calculate checksum nonce for first page
! 174: set nonce [calc_nonce 0]
! 175:
! 176: # verify all the pages in the journal use the same nonce
! 177: for {set i 1} {$i<$jrnl_npage} {incr i} {
! 178: set tnonce [calc_nonce $i]
! 179: if {$tnonce != $nonce} {
! 180: puts "WARNING: different nonces: 0=$nonce $i=$tnonce"
! 181: if {$fix_chksums } {
! 182: set jrnl_pg_offset [expr $sectsz+((4+$db_pgsz+4)*$i)]
! 183: set tchksum [calc_chksum $i]
! 184: hexio_write $jrnl_name [expr $jrnl_pg_offset+4+$db_pgsz] [format %08x $tchksum]
! 185: puts "INFO: fixing chksum: $i=$tchksum"
! 186: }
! 187: }
! 188: }
! 189:
! 190: # verify all the page numbers in the journal
! 191: for {set i 0} {$i<$jrnl_npage} {incr i} {
! 192: set jrnl_pg_offset [expr $sectsz+((4+$db_pgsz+4)*$i)]
! 193: set db_pgno [hexio_get_int [hexio_read $jrnl_name $jrnl_pg_offset 4]]
! 194: if {$db_pgno < 1} {
! 195: puts "WARNING: page number < 1: $i=$db_pgno"
! 196: }
! 197: if {$db_pgno >= $db_npage} {
! 198: puts "WARNING: page number >= $db_npage: $i=$db_pgno"
! 199: }
! 200: }
! 201:
! 202: # dump page data
! 203: if {$dump_pages} {
! 204: for {set i 0} {$i<$jrnl_npage} {incr i} {
! 205: dump_jrnl_page $i
! 206: }
! 207: }
! 208:
! 209: # write the 8 byte magic string
! 210: hexio_write $jrnl_name 0 d9d505f920a163d7
! 211:
! 212: # write -1 for number of records
! 213: hexio_write $jrnl_name 8 ffffffff
! 214:
! 215: # write 00 for checksum nonce
! 216: hexio_write $jrnl_name 12 [format %08x $nonce]
! 217:
! 218: # write page count
! 219: hexio_write $jrnl_name 16 [format %08x $db_npage]
! 220:
! 221: # write sector size
! 222: hexio_write $jrnl_name 20 [format %08x $sectsz]
! 223:
! 224: # write page size
! 225: hexio_write $jrnl_name 24 [format %08x $db_pgsz]
! 226:
! 227: # check the integrity of the database with the patched journal
! 228: sqlite3 db $db_name
! 229: do_test restore_jrnl-1.0 {
! 230: catchsql {PRAGMA integrity_check}
! 231: } {0 ok}
! 232: db close
! 233:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>