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 - co...@suse.com + +- 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; +}