1# See the file LICENSE for redistribution information. 2# 3# Copyright (c) 1996,2008 Oracle. All rights reserved. 4# 5# $Id: recd016.tcl,v 12.8 2008/01/08 20:58:53 bostic Exp $ 6# 7# TEST recd016 8# TEST Test recovery after checksum error. 9proc recd016 { method args} { 10 global fixed_len 11 global log_log_record_types 12 global datastr 13 source ./include.tcl 14 15 set pgindex [lsearch -exact $args "-pagesize"] 16 if { $pgindex != -1 } { 17 puts "Recd016: skipping for specific pagesizes" 18 return 19 } 20 if { [is_queueext $method] == 1 } { 21 puts "Recd016: skipping for method $method" 22 return 23 } 24 25 puts "Recd016: $method recovery after checksum error" 26 27 # Create the database and environment. 28 env_cleanup $testdir 29 30 set testfile recd016.db 31 set flags "-create -txn -home $testdir" 32 33 puts "\tRecd016.a: creating environment" 34 set env_cmd "berkdb_env $flags" 35 set dbenv [eval $env_cmd] 36 error_check_good dbenv [is_valid_env $dbenv] TRUE 37 38 set pgsize 512 39 set orig_fixed_len $fixed_len 40 set fixed_len [expr $pgsize / 4] 41 set opts [convert_args $method $args] 42 set omethod [convert_method $method] 43 set oflags "-create $omethod -mode 0644 \ 44 -auto_commit -chksum -pagesize $pgsize $opts $testfile" 45 set db [eval {berkdb_open} -env $dbenv $oflags] 46 47 # 48 # Put some data. 49 # 50 set nument 50 51 puts "\tRecd016.b: Put some data" 52 for { set i 1 } { $i <= $nument } { incr i } { 53 # Use 'i' as key so method doesn't matter 54 set key $i 55 set data $i$datastr 56 57 # Put, in a txn. 58 set txn [$dbenv txn] 59 error_check_good txn_begin [is_valid_txn $txn $dbenv] TRUE 60 error_check_good db_put \ 61 [$db put -txn $txn $key [chop_data $method $data]] 0 62 error_check_good txn_commit [$txn commit] 0 63 } 64 error_check_good db_close [$db close] 0 65 error_check_good log_flush [$dbenv log_flush] 0 66 error_check_good env_close [$dbenv close] 0 67 # 68 # We need to remove the env so that we don't get cached 69 # pages. 70 # 71 error_check_good env_remove [berkdb envremove -home $testdir] 0 72 73 puts "\tRecd016.c: Overwrite part of database" 74 # 75 # First just touch some bits in the file. We want to go 76 # through the paging system, so touch some data pages, 77 # like the middle of page 2. 78 # We should get a checksum error for the checksummed file. 79 # 80 set pg 2 81 set fid [open $testdir/$testfile r+] 82 fconfigure $fid -translation binary 83 set seeklen [expr $pgsize * $pg + 200] 84 seek $fid $seeklen start 85 set byte [read $fid 1] 86 binary scan $byte c val 87 set newval [expr ~$val] 88 set newbyte [binary format c $newval] 89 seek $fid $seeklen start 90 puts -nonewline $fid $newbyte 91 close $fid 92 93 # 94 # Verify we get the checksum error. When we get it, it should 95 # log the error as well, so when we run recovery we'll need to 96 # do catastrophic recovery. We do this in a sub-process so that 97 # the files are closed after the panic. 98 # 99 set f1 [open |$tclsh_path r+] 100 puts $f1 "source $test_path/test.tcl" 101 102 set env_cmd "berkdb_env_noerr $flags" 103 set dbenv [send_cmd $f1 $env_cmd] 104 error_check_good dbenv [is_valid_env $dbenv] TRUE 105 106 set db [send_cmd $f1 "{berkdb_open_noerr} -env $dbenv $oflags"] 107 error_check_good db [is_valid_db $db] TRUE 108 109 # We need to set non-blocking mode so that after each command 110 # we can read all the remaining output from that command and 111 # we can know what the output from one command is. 112 fconfigure $f1 -blocking 0 113 set ret [read $f1] 114 set got_err 0 115 for { set i 1 } { $i <= $nument } { incr i } { 116 set stat [send_cmd $f1 "catch {$db get $i} r"] 117 set getret [send_cmd $f1 "puts \$r"] 118 set ret [read $f1] 119 if { $stat == 1 } { 120 error_check_good dbget:fail [is_substr $getret \ 121 "checksum error: page $pg"] 1 122 set got_err 1 123 break 124 } else { 125 set key [lindex [lindex $getret 0] 0] 126 set data [lindex [lindex $getret 0] 1] 127 error_check_good keychk $key $i 128 error_check_good datachk $data \ 129 [pad_data $method $i$datastr] 130 } 131 } 132 error_check_good got_chksum $got_err 1 133 set ret [send_cmd $f1 "$db close"] 134 set extra [read $f1] 135 error_check_good db:fail [is_substr $ret "run recovery"] 1 136 137 set ret [send_cmd $f1 "$dbenv close"] 138 error_check_good env_close:fail [is_substr $ret "handles still open"] 1 139 close $f1 140 141 # Keep track of the log types we've seen 142 if { $log_log_record_types == 1} { 143 logtrack_read $testdir 144 } 145 146 puts "\tRecd016.d: Run normal recovery" 147 set ret [catch {exec $util_path/db_recover -h $testdir} r] 148 error_check_good db_recover $ret 1 149 error_check_good dbrec:fail \ 150 [is_substr $r "checksum error"] 1 151 152 catch {fileremove $testdir/$testfile} ret 153 puts "\tRecd016.e: Run catastrophic recovery" 154 set ret [catch {exec $util_path/db_recover -c -h $testdir} r] 155 error_check_good db_recover $ret 0 156 157 # 158 # Now verify the data was reconstructed correctly. 159 # 160 set env_cmd "berkdb_env_noerr $flags" 161 set dbenv [eval $env_cmd] 162 error_check_good dbenv [is_valid_env $dbenv] TRUE 163 164 set db [eval {berkdb_open} -env $dbenv $oflags] 165 error_check_good db [is_valid_db $db] TRUE 166 167 for { set i 1 } { $i <= $nument } { incr i } { 168 set stat [catch {$db get $i} ret] 169 error_check_good stat $stat 0 170 set key [lindex [lindex $ret 0] 0] 171 set data [lindex [lindex $ret 0] 1] 172 error_check_good keychk $key $i 173 error_check_good datachk $data [pad_data $method $i$datastr] 174 } 175 error_check_good db_close [$db close] 0 176 error_check_good log_flush [$dbenv log_flush] 0 177 error_check_good env_close [$dbenv close] 0 178 set fixed_len $orig_fixed_len 179 return 180} 181