File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sqlite3 / tool / restore_jrnl.tcl
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 17:04:17 2012 UTC (12 years, 4 months ago) by misho
Branches: sqlite3, MAIN
CVS tags: v3_7_10, HEAD
sqlite3

    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>