Annotation of embedaddon/sqlite3/tool/restore_jrnl.tcl, revision 1.1.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>