use warnings;
use File::Spec;
use Config;
-# During perl build, we need File::Copy but Fcntl might not be built yet
-my $Fcntl_loaded = eval q{ use Fcntl qw [O_CREAT O_WRONLY O_TRUNC]; 1 };
-# Similarly Scalar::Util
+# During perl build, we need File::Copy but Scalar::Util might not be built yet
# And then we need these games to avoid loading overload, as that will
# confuse miniperl during the bootstrap of perl.
my $Scalar_Util_loaded = eval q{ require Scalar::Util; require overload; 1 };
sub cp;
sub mv;
-$VERSION = '2.15';
+$VERSION = '2.19';
require Exporter;
@ISA = qw(Exporter);
my @fs = stat($from);
if (@fs) {
my @ts = stat($to);
- if (@ts && $fs[0] == $ts[0] && $fs[1] == $ts[1]) {
+ if (@ts && $fs[0] == $ts[0] && $fs[1] == $ts[1] && !-p $from) {
carp("'$from' and '$to' are identical (not copied)");
return 0;
}
}
}
- return syscopy($from, $copy_to);
+ return syscopy($from, $copy_to) || 0;
}
my $closefrom = 0;
} else {
open $from_h, "<", $from or goto fail_open1;
binmode $from_h or die "($!,$^E)";
- $closefrom = 1;
+ $closefrom = 1;
}
# Seems most logical to do this here, in case future changes would want to
if ($to_a_handle) {
$to_h = $to;
} else {
- $to = _protect($to) if $to =~ /^\s/s;
- if ($Fcntl_loaded) {
- my $perm = (stat $from_h) [2] & 0xFFF;
- sysopen $to_h, $to, O_CREAT() | O_TRUNC() | O_WRONLY(), $perm
- or goto fail_open2;
- }
- else {
- $to_h = \do { local *FH };
- open $to_h, ">", $to or goto fail_open2;
- }
+ $to_h = \do { local *FH }; # XXX is this line obsolete?
+ open $to_h, ">", $to or goto fail_open2;
binmode $to_h or die "($!,$^E)";
$closeto = 1;
}
return 0;
}
-sub move {
- croak("Usage: move(FROM, TO) ") unless @_ == 2;
-
+sub cp {
my($from,$to) = @_;
+ my(@fromstat) = stat $from;
+ my(@tostat) = stat $to;
+ my $perm;
+
+ return 0 unless copy(@_) and @fromstat;
+
+ if (@tostat) {
+ $perm = $tostat[2];
+ } else {
+ $perm = $fromstat[2] & ~(umask || 0);
+ @tostat = stat $to;
+ }
+ # Might be more robust to look for S_I* in Fcntl, but we're
+ # trying to avoid dependence on any XS-containing modules,
+ # since File::Copy is used during the Perl build.
+ $perm &= 07777;
+ if ($perm & 06000) {
+ croak("Unable to check setuid/setgid permissions for $to: $!")
+ unless @tostat;
+
+ if ($perm & 04000 and # setuid
+ $fromstat[4] != $tostat[4]) { # owner must match
+ $perm &= ~06000;
+ }
+
+ if ($perm & 02000 && $> != 0) { # if not root, setgid
+ my $ok = $fromstat[5] == $tostat[5]; # group must match
+ if ($ok) { # and we must be in group
+ $ok = grep { $_ == $fromstat[5] } split /\s+/, $)
+ }
+ $perm &= ~06000 unless $ok;
+ }
+ }
+ return 0 unless @tostat;
+ return 1 if $perm == ($tostat[2] & 07777);
+ return eval { chmod $perm, $to; } ? 1 : 0;
+}
+
+sub _move {
+ croak("Usage: move(FROM, TO) ") unless @_ == 3;
+
+ my($from,$to,$fallback) = @_;
my($fromsz,$tosz1,$tomt1,$tosz2,$tomt2,$sts,$ossts);
local $@;
eval {
local $SIG{__DIE__};
- copy($from,$to) or die;
+ $fallback->($from,$to) or die;
my($atime, $mtime) = (stat($from))[8,9];
utime($atime, $mtime, $to);
unlink($from) or die;
return 0;
}
-*cp = \©
-*mv = \&move;
+sub move { _move(@_,\©); }
+sub mv { _move(@_,\&cp); }
# &syscopy is an XSUB under OS/2
unless (defined &syscopy) {
written to (and created if need be). Trying to copy a file on top
of itself is a fatal error.
+If the destination (second argument) already exists and is a directory,
+and the source (first argument) is not a filehandle, then the source
+file will be copied into the directory specified by the destination,
+using the same base name as the source file. It's a failure to have a
+filehandle as the source when the destination is a directory.
+
B<Note that passing in
files as handles instead of names may lead to loss of information
on some operating systems; it is recommended that you use file
upon the file, but will generally be the whole file (up to 2MB), or
1k for filehandles that do not reference files (eg. sockets).
-You may use the syntax C<use File::Copy "cp"> to get at the
-"cp" alias for this function. The syntax is I<exactly> the same.
-
-As of version 2.14, on UNIX systems, "copy" will preserve permission
-bits like the shell utility C<cp> would do.
+You may use the syntax C<use File::Copy "cp"> to get at the C<cp>
+alias for this function. The syntax is I<exactly> the same. The
+behavior is nearly the same as well: as of version 2.15, <cp> will
+preserve the source file's permission bits like the shell utility
+C<cp(1)> would do, while C<copy> uses the default permissions for the
+target file (which may depend on the process' C<umask>, file
+ownership, inherited ACLs, etc.). If an error occurs in setting
+permissions, C<cp> will return 0, regardless of whether the file was
+successfully copied.
=item move
X<move> X<mv> X<rename>
during this copy-and-delete process, you may be left with a (possibly partial)
copy of the file under the destination name.
-You may use the "mv" alias for this function in the same way that
-you may use the "cp" alias for C<copy>.
+You may use the C<mv> alias for this function in the same way that
+you may use the <cp> alias for C<copy>.
=item syscopy
X<syscopy>