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