Bug in database software
- Date: Tue, 14 Jun 1994 18:33:19 +0200
Folks,
Thanks to the people from the GARR NIS and others, we have found a
bug in the database software that could affect anyone that runs
the update programs the come with the database software. It only shows
up if one uses "dbupdate", and multiple of these processes run at the
same time. A certain combination of locking and buffering can
cause data corruption. You should replace modules dbopen.pl,
dbclose.pl, dblock.pl and dbadd.pl with the new ones provided in the
shar file below.
-Marten
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: dbadd.pl dbopen.pl dbclose.pl dblock.pl
# Wrapped by marten@localhost on Tue Jun 14 18:24:43 1994
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f dbadd.pl -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"dbadd.pl\"
else
echo shar: Extracting \"dbadd.pl\" \(3073 characters\)
sed "s/^X//" >dbadd.pl <<'END_OF_dbadd.pl'
X# dbadd - add, delete objects
X#
X# $RCSfile: dbadd.pl,v $
X# $Revision: 0.19 $
X# $Author: marten $
X# $Date: 1994/05/24 15:38:24 $
X
Xrequire "dblock.pl";
Xrequire "enukey.pl";
Xrequire "enkeys.pl";
Xrequire "enwrite.pl";
Xrequire "addkey.pl";
Xrequire "dbmatch.pl";
Xrequire "defines.pl";
Xrequire "enread.pl";
Xrequire "encmp.pl";
Xrequire "updatecheck.pl";
X
Xsub dbadd {
X local(*db, *en) = @_;
X
X local($unikey) = &enukey(*en);
X
X return $E_EXIST if defined($db{$unikey});
X
X &dblock(*db);
X seek(db, 0, 2);
X select(db);
X print "\n";
X local($offset) = tell(db);
X &enwrite(*en);
X select(STDOUT);
X &addkey(*db, $unikey, $offset);
X
X foreach $i (&enkeys(*en)) {
X &addkey(*db, $i, $offset) unless $i eq $unikey;
X }
X
X &dbunlock(*db);
X
X return $OK;
X}
X
Xsub dbmodify {
X local(*db, *en) = @_;
X
X local($unikey[0]) = &enukey(*en);
X
X return $E_NOT_FOUND if !defined($db{$unikey[0]});
X
X local(@result) = &dbmatch(*db, @unikey);
X local(%curobject) = &enread(db, $result[0]);
X if (&encmp(*en, *curobject)) {
X return $E_NOOP;
X }
X
X local($updatestat) = &updatecheck(*en, *curobject);
X return $updatestat if $updatestat != $OK;
X
X local($delstat) = &dbdel(*db, *curobject);
X return $delstat if $delstat != $OK;
X
X &dblock(*db);
X seek(db, 0, 2);
X select(db);
X print "\n";
X local($offset) = tell(db);
X &enwrite(*en);
X select(STDOUT);
X seek(db,0,0);
X
X &addkey(*db, $unikey[0], $offset);
X
X foreach $i (&enkeys(*en)) {
X &addkey(*db, $i, $offset) unless $i eq $unikey[0];
X }
X
X &dbunlock(*db);
X
X return $OK;
X}
X
X
Xsub dbadd_or_modify {
X
X local(*db, *en) = @_;
X
X local($unikey[0]) = &enukey(*en);
X local(@result) = &dbmatch(*db, @unikey);
X
X if (defined $result[0]) {
X if ($#result > 1) {
X return E_MULT_MATCH;
X }
X
X local(%curobject) = &enread(db, $result[0]);
X
X local($updatestat) = &updatecheck(*en, *curobject, *db);
X return $updatestat if ($updatestat != $OK);
X
X if (&encmp(*en, *curobject)) {
X return $E_NOOP;
X }
X
X local($delstat) = &dbdel(*db, *curobject);
X return $delstat if $delstat != $OK;
X }
X
X return &dbadd(*db, *en);
X}
X
Xsub dbdel {
X local(*db, *en) = @_;
X local(@unikey);
X local(%curobject);
X local($mult) = 0;
X local($status) = $OK;
X local(%dummy) = ();
X
X $unikey[0] = &enukey(*en);
X local(@result) = &dbmatch(*db, @unikey);
X
X if ($#result > 0) {
X $mult = 1;
X }
X if ($#result < 0) {
X return $E_NOT_FOUND;
X }
X
X foreach $i (@result) {
X %curobject = &enread(db, $i);
X if (!&encmp(*en, *curobject)) {
X $status = $E_MISMATCH;
X next;
X }
X
X # Dummy updatecheck to get notification.
X # New object is null, updatecheck will recognize and
X # skip checks not done for deletes. Only do if it is
X # a true delete, and not a replace
X
X if (&hasdelete(*en)) {
X &updatecheck(*en, *dummy, *db);
X }
X
X $status = $OK;
X
X &dblock(*db);
X seek(db, $i, 0);
X print db "*XX";
X
X @keys = &enkeys(*en);
X @keys = ($unikey[0], @keys);
X for $j (0..$#keys) {
X &delkey(*db, $keys[$j], $i);
X }
X &dbunlock(*db);
X }
X
X return $status;
X}
END_OF_dbadd.pl
if test 3073 -ne `wc -c <dbadd.pl`; then
echo shar: \"dbadd.pl\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f dbopen.pl -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"dbopen.pl\"
else
echo shar: Extracting \"dbopen.pl\" \(1974 characters\)
sed "s/^X//" >dbopen.pl <<'END_OF_dbopen.pl'
X# dbopen - open a database
X#
X# $RCSfile: dbopen.pl,v $
X# $Revision: 0.14 $
X# $Author: marten $
X# $Date: 1994/01/14 14:21:31 $
X#
X# arguments:
X# $handle = file handle to dbm and flat file
X# *entry = pointer to object, determines what file to open
X# $write = open for write or not
X# $name = open a file independent of type
X
Xsub dbopen {
X
X local($handle, $name, $write) = @_;
X
X# This stuff is already new for the split databases ....
X#
X# local($handle, *entry, $write, $name) = @_;
X#
X# if (!$name) {
X# local($source) = $entry{"so"};
X# $name = $DBFILE{$source};
X# local($lockfile) = "";
X# local($type) = &entype(*entry);
X#
X# if ($TYPE{$source} eq "SPLIT") {
X# $name .= ".".$type;
X# }
X# }
X
X if ($write) {
X
X # If we want to open the dbfile for writing, make sure that
X # cleandb is not using the database. If it is there will be a
X # race condition between rename and open.
X
X # the file name used for this lockfile is $CLEANLOCK, concatenated
X # with the database filename (no full path)
X
X $name =~ /([^\/]+)$/;
X local($fileext) = $1;
X
X# And this as well for split database
X#
X# if ($TYPE{$source} eq "SPLIT") {
X# $lockfile = "$CLEANLOCK.$fileext.$type";
X# } else {
X# $lockfile = "$CLEANLOCK.$fileext";
X# }
X
X $lockfile = "$CLEANLOCK.$fileext";
X
X while (-e "$lockfile") {
X sleep 60;
X }
X if (eval "open($handle, \"+<$name\") || return 0;") {
X eval "select($handle);";
X eval "\$| = 1;";
X eval "select(STDOUT);";
X eval "dbmopen(%$handle, \"$name\", 0666);";
X eval "\$$handle\[0\] = 1;";
X eval "\$$handle\[1\] = \"$name\";";
X return 1;
X }
X else {
X return 0;
X }
X } else {
X if (eval "open($handle, \"$name\") || return 0;") {
X eval "dbmopen(%$handle, \"$name\", 0666);";
X eval "\$$handle\[0\] = 1;";
X eval "\$$handle\[1\] = \"$name\";";
X return 1;
X }
X else {
X return 0;
X }
X }
X}
X
X1;
END_OF_dbopen.pl
if test 1974 -ne `wc -c <dbopen.pl`; then
echo shar: \"dbopen.pl\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f dbclose.pl -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"dbclose.pl\"
else
echo shar: Extracting \"dbclose.pl\" \(396 characters\)
sed "s/^X//" >dbclose.pl <<'END_OF_dbclose.pl'
X# dbclose - close a database
X#
X# $RCSfile: dbclose.pl,v $
X# $Revision: 0.13 $
X# $Author: marten $
X# $Date: 1994/06/14 14:54:04 $
X
Xsub dbclose {
X
X local(*db) = @_;
X
X if ($db[0] == 1) {
X dbmclose(db);
X close(db);
X $db[0] = 0;
X }
X else {
X print STDERR "$db not open\n";
X }
X $lockcounter = 0;
X}
X
X1;
END_OF_dbclose.pl
if test 396 -ne `wc -c <dbclose.pl`; then
echo shar: \"dbclose.pl\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f dblock.pl -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"dblock.pl\"
else
echo shar: Extracting \"dblock.pl\" \(530 characters\)
sed "s/^X//" >dblock.pl <<'END_OF_dblock.pl'
X# dblock - lock and unlock of db for writing
X#
X# $RCSfile: dblock.pl,v $
X# $Revision: 0.13 $
X# $Author: marten $
X# $Date: 1994/06/14 14:55:53 $
X#
X# !!! LOCK BOTH TEXT FILE AND DBM FILE !!!
X#
X# !!! NOTE: LOCKING DOES NOT WORK ON NFS FILESYSTEMS !!!
X
Xrequire "defines.pl";
X
Xsub dblock {
X
X local(*db) = @_;
X
X flock(db, $LOCK_EX);
X flock($db, $LOCK_EX);
X $lockcounter++;
X}
X
Xsub dbunlock {
X
X local(*db) = @_;
X
X if ($lockcounter == 0) {
X flock(db, $LOCK_UN);
X flock($db, $LOCK_UN);
X } else {
X $lockcounter--;
X }
X}
X
X1;
END_OF_dblock.pl
if test 530 -ne `wc -c <dblock.pl`; then
echo shar: \"dblock.pl\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0