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>