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>