summaryrefslogtreecommitdiff
path: root/Debian/Dgit.pm
diff options
context:
space:
mode:
authorIan Jackson <ijackson@chiark.greenend.org.uk>2018-10-11 00:43:45 +0100
committerIan Jackson <ijackson@chiark.greenend.org.uk>2018-10-11 00:58:27 +0100
commitd18e956260fd206e3b0c260adeea36e697350a57 (patch)
tree9086163c5c775a6784e55dc472c56b834d6c2ce9 /Debian/Dgit.pm
parent89906fd58100567b415f9c13d83121b988dfa325 (diff)
Dgit.pm: rename_link_xf: Avoid copying if src is a link to dst
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Diffstat (limited to 'Debian/Dgit.pm')
-rw-r--r--Debian/Dgit.pm41
1 files changed, 32 insertions, 9 deletions
diff --git a/Debian/Dgit.pm b/Debian/Dgit.pm
index 19ea85b..5b1feff 100644
--- a/Debian/Dgit.pm
+++ b/Debian/Dgit.pm
@@ -434,6 +434,8 @@ sub rename_link_xf ($$$) {
# $@ to a reason message
# $! to an errno value, or -1 if not known
# having possibly printed something about mv to stderr.
+ # Not safe to use without $keeporig if $dst might be a symlink
+ # to $src, as it might delete $src leaving $dst invalid.
my ($keeporig,$src,$dst) = @_;
if ($keeporig
? link $src, $dst
@@ -444,19 +446,40 @@ sub rename_link_xf ($$$) {
$@ = "$!";
return 0;
}
- $!=0; $?=0;
- my @cmd = (qw(cp --), $src, "$dst.tmp");
- debugcmd '+',@cmd;
- if (system @cmd) {
- failedcmd_report_cmd undef, @cmd;
- $@ = failedcmd_waitstatus();
- $! = -1;
+ if (!stat $src) {
+ $@ = f_ "stat source file: %S", $!;
return 0;
}
- if (!rename "$dst.tmp", $dst) {
- $@ = f_ "finally install file after cp: %S", $!;
+ my @src_stat = (stat _)[0..1];
+
+ my @dst_stat;
+ if (stat $dst) {
+ @dst_stat = (stat _)[0..1];
+ } elsif ($! == ENOENT) {
+ } else {
+ $@ = f_ "stat destination file: %S", $!;
return 0;
}
+
+ if ("@src_stat" eq "@dst_stat") {
+ # (Symlinks to) the same file. No need for a copy but
+ # we may need to delete the original.
+ printdebug "rename_link_xf $keeporig $src $dst EXDEV but same\n";
+ } else {
+ $!=0; $?=0;
+ my @cmd = (qw(cp --), $src, "$dst.tmp");
+ debugcmd '+',@cmd;
+ if (system @cmd) {
+ failedcmd_report_cmd undef, @cmd;
+ $@ = failedcmd_waitstatus();
+ $! = -1;
+ return 0;
+ }
+ if (!rename "$dst.tmp", $dst) {
+ $@ = f_ "finally install file after cp: %S", $!;
+ return 0;
+ }
+ }
if (!$keeporig) {
if (!unlink $src) {
$@ = f_ "delete old file after cp: %S", $!;