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: $!";
+               }
        }
 }
 


Reply via email to