X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/716b46c550d12c14dc198bebafcca8ec7ebc3c76..76181461f582d385da73172d06532c4e4ec3f681:/support/atomic-rsync diff --git a/support/atomic-rsync b/support/atomic-rsync index 2f43e1ee..9e620cfe 100755 --- a/support/atomic-rsync +++ b/support/atomic-rsync @@ -1,40 +1,54 @@ #!/usr/bin/perl # # This script lets you update a hierarchy of files in an atomic way by -# first creating a new hierarchy using --link-dest to rsync, and then -# swapping the hierarchy into place. See the usage message for more -# details and some important caveats! +# first creating a new hierarchy using rsync's --link-dest option, and +# then swapping the hierarchy into place. **See the usage message for +# 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 $?; } -rename($dest_dir, $old_dir) or die "Unable to rename $new_dir to $old_dir: $!"; +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 <