Author: bdonlan
Date: 2004-06-06 17:17:24 -0400 (Sun, 06 Jun 2004)
New Revision: 230
Modified:
trunk/dev-tools/cold-backup.pl
Log:
Various fixes to make cold-backup.pl less racy
* cold-backup.pl
- Use atomic-rename and a temp file when saving dumps
- Use a lockfile over the symlink
Modified: trunk/dev-tools/cold-backup.pl
===================================================================
--- trunk/dev-tools/cold-backup.pl 2004-06-06 16:32:06 UTC (rev 229)
+++ trunk/dev-tools/cold-backup.pl 2004-06-06 21:17:24 UTC (rev 230)
@@ -9,6 +9,8 @@
use File::Spec;
use File::Basename 'basename';
use IO::Dir;
+use IO::File;
+use Fcntl qw(:flock);
use Getopt::Long;
use File::Path ();
use IPC::Open3;
@@ -17,14 +19,15 @@
# Global Settings
my %option = (
- symlink => 0,
- overwrite => 0,
+ symlink => 0,
+ overwrite => 0,
backlog => 10,
+ nolock => 0,
svnadmin => '/usr/bin/svnadmin',
svnlook => '/usr/bin/svnlook',
);
-GetOptions(\%option, 'backlog=i', 'overwrite|force', 'svnadmin=s',
'svnlook=s', 'symlink');
+GetOptions(\%option, 'backlog=i', 'overwrite|force', 'svnadmin=s',
'svnlook=s', 'symlink', 'nolock');
# Path to svnlook utility
@@ -81,6 +84,7 @@
#backup_subdir = os.path.join(backup_dir, repo + "-" + youngest)
my $backup_file = File::Spec->catfile($backup_dir, "$repo-$youngest");
+my $temp_file = File::Spec->catfile($backup_dir, ".temp.$repo-$youngest.$$");
# Normally, you don't need to re-dump if there's no revision
@@ -89,28 +93,44 @@
print "Target file already exists.\n";
exit 1;
}
-} else {
- if (-e $backup_file ) {
- unlink($backup_file)
- or die "Unable to unlink old copy of $backup_file: $!";
- }
}
### Step 3: Ask subversion to make a dump of a repository.
-my $ecode = system("(svnadmin dump '$repo_dir' > '$backup_file') 2>&1");
+my $ecode = system("(svnadmin dump -r 0:$youngest '$repo_dir' > '$temp_file')
2>&1");
if ($ecode) {
- unlink $backup_file
- or warn "Can't delete $backup_file: $!";
+ unlink $temp_file
+ or warn "Can't delete $temp_file: $!";
exit $?;
}
+unless (rename($temp_file, $backup_file)) {
+ my $rename_fail = $!;
+ warn "Can't atomic-rename $temp_file to $backup_file: $!";
+ unlink $temp_file
+ or warn "Can't unlink $temp_file: $!";
+ exit 1;
+}
+
### Step 3+1: If --symlink is given, symlink the latest version.
if ($option{symlink}) {
my $current = File::Spec->catfile($backup_dir, "$repo-current");
my $backup_file = "$repo-$youngest";
print "Symlinking $backup_file to $current (if needed)\n";
+
+ my $lock_file = File::Spec->catfile($backup_dir, ".$repo-current.lock");
+ my $lock;
+ unless ($option{nolock}) {
+ do {
+ $lock = new IO::File $lock_file, "w+"
+ or die "Can't open $lock_file: $!";
+ flock $lock, LOCK_EX;
+ # There is a possibility of a race with the unlink
+ # further below. This retries in such a case
+ } while (!-e $lock_file);
+ }
+
my $do_link = 1;
if (-e $current) {
@@ -118,7 +138,11 @@
or warn "Error reading link $current, $!\n";
if ($link ne $backup_file) {
- unlink($current) or warn "Can't unlink $current. $!\n";
+ if ($link =~ m|^$repo-(\d+)$| && $1 > $youngest) {
+ $do_link = 0;
+ } else {
+ unlink($current) or warn "Can't unlink
$current. $!\n";
+ }
} else {
$do_link = 0;
}
@@ -129,6 +153,12 @@
symlink($backup_file, $current)
or warn "Can't symlink $backup_file to $current! $!\n";
}
+
+ unless ($option{nolock}) {
+ unlink $lock_file
+ or warn "Can't unlink $lock_file: $!";
+ undef $lock;
+ }
}
### Step 4: finally, remove all repository backups other than the last
@@ -148,8 +178,9 @@
for $item (@old) {
my $old_backup_file = File::Spec->catdir($backup_dir, $item);
print "Removing old backup: $old_backup_file\n";
- unlink $old_backup_file
- or warn "Can't unlink $old_backup_file: $!";
+ unless (unlink $old_backup_file || ! -e $old_backup_file) {
+ warn "Can't unlink $old_backup_file: $!";
+ }
}
}