From 76181461f582d385da73172d06532c4e4ec3f681 Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Sat, 11 Oct 2008 09:30:26 -0700 Subject: [PATCH] Added a fully atomic update if the user has setup a symlink to a *-1 or *-2 directory. A few other minor improvements. --- support/atomic-rsync | 80 +++++++++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/support/atomic-rsync b/support/atomic-rsync index 33a493c2..9e620cfe 100755 --- a/support/atomic-rsync +++ b/support/atomic-rsync @@ -6,35 +6,49 @@ # more details and some important caveats!** use strict; +use warnings; use Cwd 'abs_path'; my $RSYNC_PROG = '/usr/bin/rsync'; my $RM_PROG = '/bin/rm'; my $dest_dir = $ARGV[-1]; -usage(1) if $dest_dir eq '' || $dest_dir =~ /^--/; +&usage if !defined $dest_dir || $dest_dir =~ /(^-|^$)/ || grep(/^--help/, @ARGV); +$dest_dir =~ s{(?<=.)/+$} {}; if (!-d $dest_dir) { - print STDERR "$dest_dir is not a directory.\n\n"; - usage(1); + die "$dest_dir is not a directory.\nUse --help for help.\n"; } -if (@_ = grep(/^--(link|compare)-dest/, @ARGV)) { +if (@_ = grep(/^--[a-z]+-dest\b/, @ARGV)) { $_ = join(' or ', @_); - print STDERR "You may not use $_ as an rsync option.\n\n"; - usage(1); + die "You cannot use the $_ option with atomic-rsync.\nUse --help for help.\n"; } +my $symlink_content = readlink $dest_dir; # undef when a real dir + +my $dest_arg = $dest_dir; +# This gives us the real destination dir, with all symlinks dereferenced. $dest_dir = abs_path($dest_dir); if ($dest_dir eq '/') { - print STDERR 'You must not use "/" as the destination directory.', "\n\n"; - usage(1); + die qq|You must not use "/" as the destination directory.\nUse --help for help.\n|; +} + +my($old_dir, $new_dir); +if (defined $symlink_content && $dest_dir =~ /-([12])$/) { + my $num = 3 - $1; + $old_dir = undef; + ($new_dir = $dest_dir) =~ s/-[12]$/-$num/; + $symlink_content =~ s/-[12]$/-$num/; +} else { + $old_dir = "$dest_dir~old~"; + $new_dir = "$dest_dir~new~"; } -my $old_dir = "$dest_dir~old~"; -my $new_dir = $ARGV[-1] = "$dest_dir~new~"; +$ARGV[-1] = "$new_dir/"; -system($RM_PROG, '-rf', $old_dir) if -d $old_dir; +system($RM_PROG, '-rf', $old_dir) if defined $old_dir && -d $old_dir; +system($RM_PROG, '-rf', $new_dir) if -d $new_dir; if (system($RSYNC_PROG, "--link-dest=$dest_dir", @ARGV)) { if ($? == -1) { @@ -48,17 +62,30 @@ if (system($RSYNC_PROG, "--link-dest=$dest_dir", @ARGV)) { exit $?; } +if (!defined $old_dir) { + atomic_symlink($symlink_content, $dest_arg); + exit; +} + rename($dest_dir, $old_dir) or die "Unable to rename $dest_dir to $old_dir: $!"; rename($new_dir, $dest_dir) or die "Unable to rename $new_dir to $dest_dir: $!"; exit; +sub atomic_symlink +{ + my($target, $link) = @_; + my $newlink = "$link~new~"; + + unlink($newlink); # Just in case + symlink($target, $newlink) or die "Unable to symlink $newlink -> $target: $!\n"; + rename($newlink, $link) or die "Unable to rename $newlink to $link: $!\n"; +} + sub usage { - my($ret) = @_; - my $fh = $ret ? *STDERR : *STDOUT; - print $fh <