#!/usr/bin/perl # # showflaw.pl # # origionally by David Harris # gensym-ized/ported to show corruption his dbm library by Stas Bekman # use strict; use DB_File; use Symbol; my $dbname = "foo.db"; $| = 1; &main; sub LOCK_SH { 1 } sub LOCK_EX { 2 } sub LOCK_NB { 4 } sub LOCK_UN { 8 } sub thread_a { print "thread_a: pid $$\n"; ## Get a lock on the database print "thread_a: attempting to open db and get lock\n"; my %db_hash; my $db_obj = tie(%db_hash, "DB_File", $dbname, O_CREAT|O_RDWR, 0644) or die "thread_a: dbcreate $dbname $!"; my $fd = $db_obj->fd; my $fh = gensym; open $fh, "+<&=$fd" or die "thread_a: fdopen $!"; flock $fh, LOCK_EX or die "thread_a: flock $!"; ## Wait five seconds print "thread_a: waiting five seconds\n"; sleep(5); ## Write a bunch of stuff to the database print "thread_a: writing 2000 records to the database\n"; foreach my $a (1 .. 20) { foreach my $b (1 .. 100) { $db_hash{"A${a}a${b}"} = "A"; } } ## Dump the database print "thread_a: dumping database to $dbname.dump01\n"; my $dump_fh = gensym; open $dump_fh, ">$dbname.dump01" or die "thread_a: open $dbname.dump01 $!"; foreach ( sort keys %db_hash ) { print $dump_fh "$_ = $db_hash{$_}\n"; } close $dump_fh; ## Release the lock print "thread_a: releasing lock\n"; $db_obj->sync(); flock $fh, LOCK_UN; undef $db_obj; untie(%db_hash); close $fh; ## Exit print "thread_a: exiting\n"; exit(0); } sub thread_b { print "thread_b: pid $$\n"; ## Wait three seconds so when we try to get a lock, thread_a already has it print "thread_b: waiting three seconds\n"; sleep(3); ## Get a lock on the database print "thread_b: attempting to open db and get lock\n"; my %db_hash; my $db_obj = tie(%db_hash, "DB_File", $dbname, O_CREAT|O_RDWR, 0644) or die "thread_b: dbcreate $dbname $!"; my $fd = $db_obj->fd; my $fh = gensym; open $fh, "+<&=$fd" or die "thread_b: fdopen $!"; flock $fh, LOCK_EX or die "thread_b: flock $!"; ## Try to dump the database print "thread_b: dumping database to $dbname.dump02\n"; my $dump_fh = gensym; open $dump_fh, ">$dbname.dump02" or die "thread_b: open $dbname.dump02 $!"; foreach ( sort keys %db_hash ) { print $dump_fh "$_ = $db_hash{$_}\n"; } close $dump_fh; ## Write a bunch of stuff to the database print "thread_b: writing 400 records to the database\n"; foreach my $a (1 .. 20) { foreach my $b (1 .. 20) { $db_hash{"B${a}b${b}"} = "B"; } } ## Try to dump the database print "thread_b: dumping database to $dbname.dump03\n"; $dump_fh = gensym; open $dump_fh, ">$dbname.dump03" or die "thread_b: open $dbname.dump03 $!"; foreach ( sort keys %db_hash ) { print $dump_fh "$_ = $db_hash{$_}\n"; } close $dump_fh; ## Release the lock print "thread_b: releasing lock\n"; $db_obj->sync(); flock $fh, LOCK_UN; undef $db_obj; untie(%db_hash); close $fh; ## Exit print "thread_b: exiting\n"; exit(0); } sub main { my %db_hash; my $db_obj; ## Clear the database print "main: creating new empty database\n"; unlink $dbname; $db_obj = tie(%db_hash, "DB_File", $dbname, O_CREAT|O_RDWR, 0644) or die "dbcreate $dbname $!"; $db_hash{"first_record"} = "stuff"; $db_obj->sync(); untie(%db_hash); undef $db_obj; ## Fork the two children print "main: forking threads and waiting for them\n"; thread_a() unless fork(); thread_b() unless fork(); wait; wait; print "main: opening and dumping database to $dbname.dump04\n"; $db_obj = tie(%db_hash, "DB_File", $dbname, O_CREAT|O_RDWR, 0644) or die "dbcreate $dbname $!"; open DUMP, ">$dbname.dump04" or die "thread_b: open $dbname.dump04 $!"; foreach ( sort keys %db_hash ) { print DUMP "$_ = $db_hash{$_}\n"; } close DUMP; $db_obj->sync(); untie(%db_hash); undef $db_obj; ## Exit #print `dd if=$dbname bs=1024 count=4 2>/dev/null | md5sum`; print "main: exiting\n"; exit 0; }