Hello community,
here is the log from the commit of package perl-File-Path-Tiny for
openSUSE:Factory checked in at 2018-02-09 15:47:39
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/perl-File-Path-Tiny (Old)
and /work/SRC/openSUSE:Factory/.perl-File-Path-Tiny.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "perl-File-Path-Tiny"
Fri Feb 9 15:47:39 2018 rev:10 rq:573990 version:0.9
Changes:
--------
--- /work/SRC/openSUSE:Factory/perl-File-Path-Tiny/perl-File-Path-Tiny.changes
2016-02-17 12:19:48.000000000 +0100
+++
/work/SRC/openSUSE:Factory/.perl-File-Path-Tiny.new/perl-File-Path-Tiny.changes
2018-02-09 15:47:40.455114948 +0100
@@ -1,0 +2,13 @@
+Wed Feb 7 16:31:14 UTC 2018 - [email protected]
+
+- updated to 0.9
+ see /usr/share/doc/packages/perl-File-Path-Tiny/Changes
+
+ 0.9 2017-12-29 16:17:42
+ - Do chdir() for security reasons by default
+ - Stop emptying/removing a path if it is changed out from underneath us
+ - rt 96843: Add SEE ALSO section
+ - prereq and unit test fixups
+ - Unit tests for symlink toggling protection
+
+-------------------------------------------------------------------
Old:
----
File-Path-Tiny-0.8.tar.gz
New:
----
File-Path-Tiny-0.9.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ perl-File-Path-Tiny.spec ++++++
--- /var/tmp/diff_new_pack.axmMiY/_old 2018-02-09 15:47:41.251086365 +0100
+++ /var/tmp/diff_new_pack.axmMiY/_new 2018-02-09 15:47:41.255086221 +0100
@@ -1,7 +1,7 @@
#
# spec file for package perl-File-Path-Tiny
#
-# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -17,19 +17,21 @@
Name: perl-File-Path-Tiny
-Version: 0.8
+Version: 0.9
Release: 0
%define cpan_name File-Path-Tiny
Summary: Recursive Versions of Mkdir() and Rmdir() Without As Much
Overhead As Fi[cut]
License: Artistic-1.0 or GPL-1.0+
Group: Development/Libraries/Perl
Url: http://search.cpan.org/dist/File-Path-Tiny/
-Source0:
http://www.cpan.org/authors/id/D/DM/DMUEY/%{cpan_name}-%{version}.tar.gz
+Source0:
https://cpan.metacpan.org/authors/id/D/DM/DMUEY/%{cpan_name}-%{version}.tar.gz
Source1: cpanspec.yml
BuildArch: noarch
BuildRoot: %{_tmppath}/%{name}-%{version}-build
BuildRequires: perl
BuildRequires: perl-macros
+BuildRequires: perl(Test::Exception)
+Requires: perl(Test::Exception)
%{perl_requires}
%description
@@ -53,14 +55,20 @@
* * chdir()s
-It does a ton of chdir()s which could leave you somewhere you're not
+It forces a ton of chdir()s which could leave you somewhere you're not
planning on being and requires much more overhead to do.
+This module provides a way to disable that if you know it is safe to do so
+in your circumstance.
+
* * can croak not allowing you to detect and handle failure
Just let me handle errors how I want. Don't make my entire app die or have
to wrap it in an eval
+The exception here is the security checks can croak, which is what you
+want. See DIAGNOSTICS for more info.
+
* * A well intentioned output system
Just let me do the output how I want. (Nothing, As HTML, print to a
++++++ File-Path-Tiny-0.8.tar.gz -> File-Path-Tiny-0.9.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/File-Path-Tiny-0.8/Changes
new/File-Path-Tiny-0.9/Changes
--- old/File-Path-Tiny-0.8/Changes 2016-01-26 21:48:41.000000000 +0100
+++ new/File-Path-Tiny-0.9/Changes 2017-12-29 23:18:10.000000000 +0100
@@ -1,5 +1,12 @@
Revision history for File-Path-Tiny
+0.9 2017-12-29 16:17:42
+ - Do chdir() for security reasons by default
+ - Stop emptying/removing a path if it is changed out from underneath us
+ - rt 96843: Add SEE ALSO section
+ - prereq and unit test fixups
+ - Unit tests for symlink toggling protection
+
0.8 2016-01-26 14:44:57
- rt 88849: allow multiple processes to operate on the same paths (thanks
HELENA!)
- Add github to POD
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/File-Path-Tiny-0.8/MANIFEST
new/File-Path-Tiny-0.9/MANIFEST
--- old/File-Path-Tiny-0.8/MANIFEST 2013-09-27 01:44:41.000000000 +0200
+++ new/File-Path-Tiny-0.9/MANIFEST 2017-12-29 15:44:23.000000000 +0100
@@ -5,6 +5,7 @@
lib/File/Path/Tiny.pm
lib/File/Path/Tiny.pod
t/00.load.t
+t/01.symtogsafe.t
t/perlcritic.t
t/perltidy.t
t/pkg-changes.t
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/File-Path-Tiny-0.8/META.json
new/File-Path-Tiny-0.9/META.json
--- old/File-Path-Tiny-0.8/META.json 2016-01-26 21:49:47.000000000 +0100
+++ new/File-Path-Tiny-0.9/META.json 2017-12-29 23:21:26.000000000 +0100
@@ -4,7 +4,7 @@
"Daniel Muey <http://drmuey.com/cpan_contact.pl>"
],
"dynamic_config" : 1,
- "generated_by" : "ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter
version 2.150001",
+ "generated_by" : "ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter
version 2.150001",
"license" : [
"perl_5"
],
@@ -32,10 +32,14 @@
},
"runtime" : {
"requires" : {
+ "Carp" : "0",
+ "Cwd" : "0",
+ "File::Temp" : "0",
+ "Test::Exception" : "0",
"Test::More" : "0"
}
}
},
"release_status" : "stable",
- "version" : "0.8"
+ "version" : "0.9"
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/File-Path-Tiny-0.8/META.yml
new/File-Path-Tiny-0.9/META.yml
--- old/File-Path-Tiny-0.8/META.yml 2016-01-26 21:49:46.000000000 +0100
+++ new/File-Path-Tiny-0.9/META.yml 2017-12-29 23:21:26.000000000 +0100
@@ -7,7 +7,7 @@
configure_requires:
ExtUtils::MakeMaker: '0'
dynamic_config: 1
-generated_by: 'ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version
2.150001'
+generated_by: 'ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version
2.150001'
license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -18,5 +18,9 @@
- t
- inc
requires:
+ Carp: '0'
+ Cwd: '0'
+ File::Temp: '0'
+ Test::Exception: '0'
Test::More: '0'
-version: '0.8'
+version: '0.9'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/File-Path-Tiny-0.8/Makefile.PL
new/File-Path-Tiny-0.9/Makefile.PL
--- old/File-Path-Tiny-0.8/Makefile.PL 2013-09-27 01:56:30.000000000 +0200
+++ new/File-Path-Tiny-0.9/Makefile.PL 2017-12-29 21:13:08.000000000 +0100
@@ -10,7 +10,11 @@
PL_FILES => {},
LICENSE => 'perl',
PREREQ_PM => {
- 'Test::More' => 0,
+ 'Test::More' => 0, # for unit tests
+ 'File::Temp' => 0, # for unit tests
+ 'Test::Exception' => 0, # for unit tests
+ 'Cwd' => 0,
+ 'Carp' => 0,
},
dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
clean => { FILES => 'File-Path-Tiny-*' },
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/File-Path-Tiny-0.8/README
new/File-Path-Tiny-0.9/README
--- old/File-Path-Tiny-0.8/README 2016-01-26 21:45:59.000000000 +0100
+++ new/File-Path-Tiny-0.9/README 2017-12-29 23:18:25.000000000 +0100
@@ -1,4 +1,4 @@
-File-Path-Tiny version 0.8
+File-Path-Tiny version 0.9
DOCUMENTATION
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/File-Path-Tiny-0.8/lib/File/Path/Tiny.pm
new/File-Path-Tiny-0.9/lib/File/Path/Tiny.pm
--- old/File-Path-Tiny-0.8/lib/File/Path/Tiny.pm 2016-01-26
21:45:37.000000000 +0100
+++ new/File-Path-Tiny-0.9/lib/File/Path/Tiny.pm 2017-12-29
23:18:39.000000000 +0100
@@ -2,8 +2,10 @@
use strict;
use warnings;
+use Cwd qw(cwd chdir);
+use Carp ();
-$File::Path::Tiny::VERSION = 0.8;
+$File::Path::Tiny::VERSION = 0.9;
sub mk {
my ( $path, $mask ) = @_;
@@ -30,30 +32,56 @@
}
sub rm {
- my ($path) = @_;
- if ( -e $path && !-d $path ) { $! = 20; return; }
- return 2 if !-d $path;
- empty_dir($path) or return;
+ my ( $path, $fast ) = @_;
+ my ( $orig_dev, $orig_ino ) = ( lstat $path )[ 0, 1 ];
+ if ( -e _ && !-d _ ) { $! = 20; return; }
+ return 2 if !-d _;
+
+ empty_dir( $path, $fast ) or return;
+ _bail_if_changed( $path, $orig_dev, $orig_ino );
rmdir($path) or !-e $path or return;
return 1;
}
sub empty_dir {
- my ($path) = @_;
- if ( -e $path && !-d $path ) { $! = 20; return; }
+ my ( $path, $fast ) = @_;
+ my ( $orig_dev, $orig_ino ) = ( lstat $path )[ 0, 1 ];
+ if ( -e _ && !-d _ ) { $! = 20; return; }
+
+ my ( $starting_point, $starting_dev, $starting_ino );
+ if ( !$fast ) {
+ $starting_point = cwd();
+ ( $starting_dev, $starting_ino ) = ( lstat $starting_point )[ 0, 1 ];
+ chdir($path) or Carp::croak("Failed to change directory to “$path”:
$!");
+ $path = '.';
+ _bail_if_changed( $path, $orig_dev, $orig_ino );
+ }
+
opendir( DIR, $path ) or return;
my @contents = grep { $_ ne '.' && $_ ne '..' } readdir(DIR);
closedir DIR;
+ _bail_if_changed( $path, $orig_dev, $orig_ino );
+
require File::Spec if @contents;
for my $thing (@contents) {
my $long = File::Spec->catdir( $path, $thing );
- if ( !-l $long && -d $long ) {
+ if ( !-l $long && -d _ ) {
+ _bail_if_changed( $path, $orig_dev, $orig_ino );
rm($long) or !-e $long or return;
}
else {
+ _bail_if_changed( $path, $orig_dev, $orig_ino );
unlink $long or !-e $long or return;
}
}
+
+ _bail_if_changed( $path, $orig_dev, $orig_ino );
+
+ if ( !$fast ) {
+ chdir($starting_point) or Carp::croak("Failed to change directory to
“$starting_point”: $!");
+ _bail_if_changed( ".", $starting_dev, $starting_ino );
+ }
+
return 1;
}
@@ -73,4 +101,23 @@
return mk( $parent, $mode );
}
+sub _bail_if_changed {
+ my ( $path, $orig_dev, $orig_ino ) = @_;
+
+ my ( $cur_dev, $cur_ino ) = ( lstat $path )[ 0, 1 ];
+
+ if ( !defined $cur_dev || !defined $cur_ino ) {
+ $cur_dev ||= "undef(path went away?)";
+ $cur_ino ||= "undef(path went away?)";
+ }
+ else {
+ $path = Cwd::abs_path($path);
+ }
+
+ if ( $orig_dev ne $cur_dev || $orig_ino ne $cur_ino ) {
+ local $Carp::CarpLevel += 1;
+ Carp::croak("directory $path changed: expected dev=$orig_dev
ino=$orig_ino, actual dev=$cur_dev ino=$cur_ino, aborting");
+ }
+}
+
1;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/File-Path-Tiny-0.8/lib/File/Path/Tiny.pod
new/File-Path-Tiny-0.9/lib/File/Path/Tiny.pod
--- old/File-Path-Tiny-0.8/lib/File/Path/Tiny.pod 2016-01-26
21:45:51.000000000 +0100
+++ new/File-Path-Tiny-0.9/lib/File/Path/Tiny.pod 2017-12-29
23:18:46.000000000 +0100
@@ -6,7 +6,7 @@
=head1 VERSION
-This document describes File::Path::Tiny version 0.8
+This document describes File::Path::Tiny version 0.9
=head1 SYNOPSIS
@@ -38,12 +38,16 @@
=item * chdir()s
-It does a ton of chdir()s which could leave you somewhere you're not planning
on being and requires much more overhead to do.
+It forces a ton of chdir()s which could leave you somewhere you're not
planning on being and requires much more overhead to do.
+
+This module provides a way to disable that if you know it is safe to do so in
your circumstance.
=item * can croak not allowing you to detect and handle failure
Just let me handle errors how I want. Don't make my entire app die or have to
wrap it in an eval
+The exception here is the security checks can croak, which is what you want.
See L<DIAGNOSTICS> for more info.
+
=item * A well intentioned output system
Just let me do the output how I want. (Nothing, As HTML, print to a
filehandle, etc...)
@@ -76,28 +80,50 @@
Takes a single path (like L<rmdir>()) to recursively empty and remove.
-Returns false if it could not be emptied or removed, true otherwise. (returns
‘2’ if it is !-d already)
+Returns false if it could not be emptied or removed, true otherwise (returns
‘2’ if it is !-d already). Also see L<DIAGNOSTICS>.
It is recursive in the sense that given “/foo/bar/baz” as the path to remove
it will recursively empty ‘baz’ and then remove it from /foo/bar.
Its parents, /, /foo, and /foo/bar are *not* touched.
+By default it does chdir() for security reasons. If you are sure it is safe to
do without that for the sake of a bit of speed you can pass a second true
argument to skip that.
+
=head2 File::Path::Tiny::empty_dir()
-Takes a single path to recursively empty but not remove.
+Takes a single path to recursively empty but not remove.
-Returns false when there is a problem.
+Returns false when there is a problem. Also see L<DIAGNOSTICS>.
+
+By default it does chdir() for security reasons. If you are sure it is safe to
do without that for the sake of a bit of speed you can pass a second true
argument to skip that.
=head2 File::Path::Tiny::mk_parent()
Like mk() but recursively creates the parent. e.g. given “foo/bar/baz.txt”
creates foo/bar.
-=head1 DIAGNOSTICS
+=head2 From Cwd
-Throws no warnings or errors of its own
+It uses these internally so, for convenience, these are exposed in case you
want to use them also.
+
+=head3 File::Path::Tiny::cwd()
+
+Comes directly from L<Cwd’s cwd()|Cwd/cwd>.
+
+=head3 File::Path::Tiny::chdir()
+
+Comes directly from L<Cwd’s chdir()|Cwd/$ENV{PWD}>.
+
+=head1 DIAGNOSTICS
If the functions ever return false, $! will have been set either explicitly or
by the L<mkdir>(), L<rmdir>(), L<unlink>(), or L<opendir>() that ultimately
returned false.
+=over 4
+
+=item C<< directory %s changed: expected dev=%d ino=$d, actual dev=%d ino=%d,
aborting >>
+
+empty_dir() and rm() throw this if any of the directories being operated on
change during the operation.
+
+=back
+
=head1 MISC
=head2 How can I make/remove multiple paths?
@@ -175,6 +201,10 @@
L<File::Spec> of course but its only loaded if needed
+=head1 SEE ALSO
+
+We already talked about L<File::Path> in the L</DESCRIPTION>. L<Path::Tiny>
also offers a mkpath interface but it too has/does things that this module
attempts to do without per the L</DESCRIPTION>. Plus its ::Tiny name is a
misnomer, see L<Acme::Tiny> for details.
+
=head1 INCOMPATIBILITIES
None reported.
@@ -216,4 +246,4 @@
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
\ No newline at end of file
+SUCH DAMAGES.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/File-Path-Tiny-0.8/t/00.load.t
new/File-Path-Tiny-0.9/t/00.load.t
--- old/File-Path-Tiny-0.8/t/00.load.t 2013-09-27 01:56:31.000000000 +0200
+++ new/File-Path-Tiny-0.9/t/00.load.t 2017-12-29 23:19:06.000000000 +0100
@@ -1,6 +1,9 @@
-use Test::More tests => 30;
+use strict;
+use warnings;
-use lib '../lib';
+use Test::More tests => 30;
+use File::Temp;
+use Cwd;
BEGIN {
use_ok('File::Path::Tiny');
@@ -8,49 +11,37 @@
diag("Testing File::Path::Tiny $File::Path::Tiny::VERSION");
-# cleanup from last time
-
-for my $path (
- qw(
- foo/bar/mode foo/bar/mode2 foo/bar/mode3
- foo/bar/mode_mkdir foo/bar/mode_mkdir2 foo/bar/dir
- foo/bar/file foo/bar foo
- zib/dib zib parent_mode parent_oct parent_str
- )
- ) {
- if ( !-l $path && -d $path ) {
- rmdir $path;
- }
- else {
- unlink $path;
- }
-}
+note "Relative paths";
+{
+ my $dir = File::Temp->newdir;
+ my $starting_dir = cwd();
+ chdir $dir || die "Could not change into temp dir: $!";
-SKIP: {
- skip 'Stale testing files exist', 5 if -d 'foo/bar';
ok( File::Path::Tiny::mk("foo/bar"), "make simple path - return true" );
ok( -d "foo/bar", "make simple path - path recursively
created" );
ok( File::Path::Tiny::mk("foo") == 2, "make already existing dir" );
- if ( open my $fh, '>', 'foo/bar/file' ) {
+
+ if ( open my $fh, '>', "foo/bar/file" ) {
print {$fh} "test";
close $fh;
}
- SKIP: {
- skip 'test file not created', 2 if !-e 'foo/bar/file';
- ok( !File::Path::Tiny::mk("foo/bar/file"), "make already existing non
dir - return false" );
- ok( $! == 20, "make already existing file
- errno" );
+ else {
+ die "test file not created: $!";
}
+ ok( !File::Path::Tiny::mk("foo/bar/file"), "make already existing non dir
- return false" );
+ ok( $! == 20, "make already existing file -
errno" );
my $file = "zib/dib/kib";
ok( File::Path::Tiny::mk_parent($file), "mk_parent() simple path
returns true" );
ok( -d "zib/dib", "mk_parent() simple path -
parent recursively created" );
ok( !-e $file, "mk_parent() simple path -
file not created" );
ok( File::Path::Tiny::mk_parent($file) == 2, "mk_parent() already existing
simple path dir" );
+
+ chdir $starting_dir || die "Could not go back into $starting_dir: $!\n";
}
-SKIP: {
- eval 'require File::Temp;';
- skip 'Absolute path test/mk_parent requires File::Temp', 3 if $@;
+note "Absolute paths";
+{
my $dir = File::Temp->newdir();
my $new = "$dir/foo/bar/baz";
ok( File::Path::Tiny::mk($new), "make absolute path - return true" );
@@ -64,40 +55,56 @@
ok( File::Path::Tiny::mk_parent($file) == 2, "mk_parent() already existing
absolute path dir" );
}
-mkdir 'foo/bar/dir';
+note "Modes and misc";
+{
-my $mk_mode = ( stat('foo/bar') )[2];
+ my $dir = File::Temp->newdir;
+ my $starting_dir = cwd();
+ chdir $dir || die "Could not change into temp dir: $!";
+ File::Path::Tiny::mk('foo/bar/dir');
+
+ my $mk_mode = ( stat('foo/bar') )[2];
+
+ # $mk_mode = sprintf('%04o', $mk_mode & 07777);
+ my $mkdir_mode = ( stat('foo/bar/dir') )[2];
+
+ # $mkdir_mode = sprintf('%04o', $mkdir_mode & 07777);
+ # diag("mk: $mk_mode, mkdir: $mkdir_mode");
+ ok( $mk_mode == $mkdir_mode, 'MASK logic gets same results as mkdir()' );
+
+ File::Path::Tiny::mk( "foo/bar/mode", 0700 );
+ File::Path::Tiny::mk_parent( "parent_mode/x", 0700 );
+ mkdir 'foo/bar/mode_mkdir', 0700;
+ ok( ( stat('foo/bar/mode') )[2] == ( stat('foo/bar/mode_mkdir') )[2],
'MASK arg OCT gets same results as mkdir()' );
+ ok( ( stat('parent_mode') )[2] == ( stat('foo/bar/mode_mkdir') )[2],
'MASK arg OCT gets same results as mkdir() - mk_parent()' );
+
+ File::Path::Tiny::mk( "foo/bar/mode2", oct('0700') );
+ File::Path::Tiny::mk_parent( "parent_oct/x", oct('0700') );
+ mkdir 'foo/bar/mode_mkdir2', oct('0700');
+ ok( ( stat('foo/bar/mode2') )[2] == ( stat('foo/bar/mode_mkdir2') )[2],
'MASK arg oct(STR) gets same results as mkdir()' );
+ ok( ( stat('parent_oct') )[2] == ( stat('foo/bar/mode_mkdir2') )[2],
'MASK arg oct(STR) gets same results as mkdir() - mk_parent()' );
+
+ File::Path::Tiny::mk( "foo/bar/mode3", "0700" );
+ File::Path::Tiny::mk_parent( "parent_str/x", "0700" );
+
+ # mkdir 'foo/bar/mode_mkdir3', "0700"; # this breaks permissions so we
compare with previous one
+ ok( ( stat('foo/bar/mode3') )[2] == ( stat('foo/bar/mode2') )[2], 'MASK
arg STR gets detected and handled - different results as mkdir()' );
+ ok( ( stat('parent_str') )[2] == ( stat('foo/bar/mode2') )[2], 'MASK
arg STR gets detected and handled - different results as mkdir() - mk_parent()'
);
-# $mk_mode = sprintf('%04o', $mk_mode & 07777);
-my $mkdir_mode = ( stat('foo/bar/dir') )[2];
+ if ( open my $fh, '>', "foo/bar/file" ) {
+ print {$fh} "test";
+ close $fh;
+ }
+ else {
+ die "test file not created: $!";
+ }
+ ok( !File::Path::Tiny::rm("foo/bar/file"), "remove existing non dir -
return false" );
+ ok( $! == 20, "remove existing non dir -
errno" );
+ undef $!;
+ ok( File::Path::Tiny::rm('foo/bar'), "empty and remove simple path -
return true" );
+ ok( !-d 'foo/bar', "remove simple path - path
recursively removed" );
+ ok( File::Path::Tiny::rm('foo/bar') == 2, "remove already non-existing
dir" );
+ ok( File::Path::Tiny::rm('foo'), 'remove empty dir' );
-# $mkdir_mode = sprintf('%04o', $mkdir_mode & 07777);
-# diag("mk: $mk_mode, mkdir: $mkdir_mode");
-ok( $mk_mode == $mkdir_mode, 'MASK logic gets same results as mkdir()' );
-
-File::Path::Tiny::mk( "foo/bar/mode", 0700 );
-File::Path::Tiny::mk_parent( "parent_mode/x", 0700 );
-mkdir 'foo/bar/mode_mkdir', 0700;
-ok( ( stat('foo/bar/mode') )[2] == ( stat('foo/bar/mode_mkdir') )[2], 'MASK
arg OCT gets same results as mkdir()' );
-ok( ( stat('parent_mode') )[2] == ( stat('foo/bar/mode_mkdir') )[2], 'MASK
arg OCT gets same results as mkdir() - mk_parent()' );
-
-File::Path::Tiny::mk( "foo/bar/mode2", oct('0700') );
-File::Path::Tiny::mk_parent( "parent_oct/x", oct('0700') );
-mkdir 'foo/bar/mode_mkdir2', oct('0700');
-ok( ( stat('foo/bar/mode2') )[2] == ( stat('foo/bar/mode_mkdir2') )[2], 'MASK
arg oct(STR) gets same results as mkdir()' );
-ok( ( stat('parent_oct') )[2] == ( stat('foo/bar/mode_mkdir2') )[2], 'MASK
arg oct(STR) gets same results as mkdir() - mk_parent()' );
-
-File::Path::Tiny::mk( "foo/bar/mode3", "0700" );
-File::Path::Tiny::mk_parent( "parent_str/x", "0700" );
-
-# mkdir 'foo/bar/mode_mkdir3', "0700"; # this breaks permissions so we compare
with previous one
-ok( ( stat('foo/bar/mode3') )[2] == ( stat('foo/bar/mode2') )[2], 'MASK arg
STR gets detected and handled - different results as mkdir()' );
-ok( ( stat('parent_str') )[2] == ( stat('foo/bar/mode2') )[2], 'MASK arg
STR gets detected and handled - different results as mkdir() - mk_parent()' );
-
-ok( !File::Path::Tiny::rm("foo/bar/file"), "remove existing non dir - return
false" );
-ok( $! == 20, "remove existing non dir - errno" );
-undef $!;
-ok( File::Path::Tiny::rm('foo/bar'), "empty and remove simple path -
return true" );
-ok( !-d 'foo/bar', "remove simple path - path
recursively removed" );
-ok( File::Path::Tiny::rm('foo/bar') == 2, "remove already non-existing dir" );
-ok( File::Path::Tiny::rm('foo'), 'remove empty dir' );
+ chdir $starting_dir || die "Could not go back into $starting_dir: $!\n";
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/File-Path-Tiny-0.8/t/01.symtogsafe.t
new/File-Path-Tiny-0.9/t/01.symtogsafe.t
--- old/File-Path-Tiny-0.8/t/01.symtogsafe.t 1970-01-01 01:00:00.000000000
+0100
+++ new/File-Path-Tiny-0.9/t/01.symtogsafe.t 2017-12-29 23:19:06.000000000
+0100
@@ -0,0 +1,133 @@
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Exception;
+
+use File::Path::Tiny;
+
+if ( !-x "/bin/mv" || !-x "/bin/mkdir" ) { # dragons! patches welcome
+ plan skip_all => 'Only operate on systems w/ /bin/mv and /bin/mkdir, for
reasons see the cource code comments';
+}
+else {
+ plan tests => 22;
+}
+
+use File::Temp;
+use Cwd;
+use File::Spec;
+
+my $orig_dir = Cwd::cwd();
+my $dir = File::Temp->newdir();
+our $catdir_toggle = sub { };
+our @catdir_calls;
+
+chdir $dir || die "Could not chdir into temp directory: $!\n"; # so we can
pathrm(), dragons!
+
+{
+
##############################################################################
+ #### Wrap catdir() to control a symlink toggle in the path traversal
loops. ##
+
##############################################################################
+ no strict "refs";
+ no warnings "redefine", "once";
+ my $real_catdir = \&{ $File::Spec::ISA[0] . "::catdir" };
+ local *File::Spec::catdir = sub {
+ my ( $self, @args ) = @_;
+ push @catdir_calls, \@args;
+ $catdir_toggle->(@args);
+ goto &$real_catdir;
+ };
+
+ mkdir "empty_dir";
+ mkdir "empty_dir/sanity";
+ File::Path::Tiny::empty_dir("empty_dir");
+ is( @catdir_calls, 1, "sanity check: catdir was actually called in the
empty_dir() loop" );
+
+ mkdir "rm";
+ mkdir "rm/sanity";
+ File::Path::Tiny::rm("rm");
+ is( @catdir_calls, 2, "sanity check: catdir was actually called in the
pathrmdir() loop" );
+
+ ####################
+ #### Actual tests ##
+ ####################
+
+ for my $func (qw(empty_dir rm)) {
+ _test( $func, "cwd/foo/bar/baz", "bails when high level changes" );
+ _test( $func, "cwd/foo/bar", "bails when mid level changes" );
+ _test( $func, "cwd/foo", "bails when low level changes" );
+ _test( $func, "cwd", "bails when CWD level changes" );
+ _test( $func, "", "bails when below level changes" );
+ }
+
+ # TODO: cover readdir, chdir, and post loop failures
+}
+
+chdir $orig_dir || die "Could not chdir back to original directory: $!\n";
+
+###############
+#### helpers ##
+###############
+
+sub _test {
+ my ( $func, $toggle, $label ) = @_;
+
+ _setup_tree($func);
+
+ {
+ local @catdir_calls = ();
+ local $catdir_toggle = sub {
+ chdir $dir || die "could not toggle dir/symlink (chdir): $!";
+
+ my $parent = "";
+ if ($toggle) {
+ $parent = $toggle;
+ $parent =~ s{[^/]+$}{};
+
+ # use system call since the perl to do this will likely use
File::Spec
+ system("/bin/mkdir -p moved/$func/$parent") and die "could not
toggle dir/symlink (mkdir): $?\n";
+ }
+
+ # use system call since the perl to do this will likely use
File::Spec
+ system("/bin/mv $dir/$func/$toggle $dir/moved/$func/$toggle") and
die "could not toggle dir/symlink (mv): $?\n";
+ symlink( "$dir/victim", "$dir/$func" . ( $toggle ? "/$toggle" : ""
) ) or die "could not toggle dir/symlink (sym): $!\n";
+
+ chdir "$func/cwd" || die "could not toggle dir/symlink (back into
$func/cwd): $!\n";
+ };
+
+ throws_ok { no strict "refs";
"File::Path::Tiny::$func"->("foo/bar/baz") }
+ qr/directory .* changed: expected dev=.* ino=.*, actual dev=.* ino=.*,
aborting/,
+ "$func() detected symlink toggle: $label";
+
+ is( @catdir_calls, 1, "sanity check: catdir was actually called in
$func() ($label)" );
+ }
+
+ _teardown_tree($func);
+}
+
+sub _teardown_tree {
+ my ($base) = @_;
+
+ chdir $dir || die "Could not chdir back into temp dir: $!\n";
+
+ File::Path::Tiny::rm($base);
+ File::Path::Tiny::rm("moved/");
+ File::Path::Tiny::rm("victim/");
+
+ return;
+}
+
+sub _setup_tree {
+ my ($base) = @_;
+
+ for my $dir ( "moved", "victim", "victim/cwd", $base, "$base/cwd",
"$base/cwd/foo", "$base/cwd/foo/bar", "$base/cwd/foo/bar/baz" ) {
+ mkdir $dir || die "Could not make test tree ($dir): $!\n";
+ open my $fh, ">", "$dir/file.txt" || die "Could not make test file in
($dir): $!\n";
+ print {$fh} "oh hai\n";
+ close($fh);
+ }
+
+ chdir "$base/cwd" || die "Could not chdir into $base/cwd: $!\n";
+
+ return;
+}