summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/File/Find.pm740
-rwxr-xr-xt/lib/filefind.t735
2 files changed, 1127 insertions, 348 deletions
diff --git a/lib/File/Find.pm b/lib/File/Find.pm
index 0ff767fc72..9ae39ace5d 100644
--- a/lib/File/Find.pm
+++ b/lib/File/Find.pm
@@ -47,19 +47,24 @@ specifying C<{ bydepth => 1 }> in the first argument of find().
=item C<preprocess>
-The value should be a code reference. This code reference is used to
-preprocess a directory; it is called after readdir() but before the loop that
-calls the wanted() function. It is called with a list of strings and is
-expected to return a list of strings. The code can be used to sort the
-strings alphabetically, numerically, or to filter out directory entries based
-on their name alone.
+The value should be a code reference. This code reference is used to
+preprocess the current directory. The name of currently processed
+directory is in $File::Find::dir. Your preprocessing function is
+called after readdir() but before the loop that calls the wanted()
+function. It is called with a list of strings (actually file/directory
+names) and is expected to return a list of strings. The code can be
+used to sort the file/directory names alphabetically, numerically,
+or to filter out directory entries based on their name alone. When
+I<follow> or I<follow_fast> are in effect, C<preprocess> is a no-op.
=item C<postprocess>
-The value should be a code reference. It is invoked just before leaving the
-current directory. It is called in void context with no arguments. The name
-of the current directory is in $File::Find::dir. This hook is handy for
-summarizing a directory, such as calculating its disk usage.
+The value should be a code reference. It is invoked just before leaving
+the currently processed directory. It is called in void context with no
+arguments. The name of the current directory is in $File::Find::dir. This
+hook is handy for summarizing a directory, such as calculating its disk
+usage. When I<follow> or I<follow_fast> are in effect, C<preprocess> is a
+no-op.
=item C<follow>
@@ -101,7 +106,7 @@ are about to be processed a second time, File::Find dies.
C<follow_skip==0> causes File::Find to die if any file is about to be
processed a second time.
C<follow_skip==2> causes File::Find to ignore any duplicate files and
-dirctories but to proceed normally otherwise.
+directories but to proceed normally otherwise.
=item C<no_chdir>
@@ -114,9 +119,10 @@ C<$_> will be the same as C<$File::Find::name>.
If find is used in taint-mode (-T command line switch or if EUID != UID
or if EGID != GID) then internally directory names have to be untainted
-before they can be cd'ed to. Therefore they are checked against a regular
-expression I<untaint_pattern>. Note that all names passed to the
-user's I<wanted()> function are still tainted.
+before they can be chdir'ed to. Therefore they are checked against a regular
+expression I<untaint_pattern>. Note that all names passed to the user's
+I<wanted()> function are still tainted. If this option is used while
+not in taint-mode, C<untaint> is a no-op.
=item C<untaint_pattern>
@@ -126,8 +132,8 @@ Note that the parantheses are vital.
=item C<untaint_skip>
-If set, directories (subtrees) which fail the I<untaint_pattern>
-are skipped. The default is to 'die' in such a case.
+If set, a directory which fails the I<untaint_pattern> is skipped,
+including all its sub-directories. The default is to 'die' in such a case.
=back
@@ -136,7 +142,7 @@ C<$File::Find::dir> contains the current directory name, and C<$_> the
current filename within that directory. C<$File::Find::name> contains
the complete pathname to the file. You are chdir()'d to
C<$File::Find::dir> when the function is called, unless C<no_chdir>
-was specified. When <follow> or <follow_fast> are in effect, there is
+was specified. When C<follow> or C<follow_fast> are in effect, there is
also a C<$File::Find::fullname>. The function may set
C<$File::Find::prune> to prune the tree unless C<bydepth> was
specified. Unless C<follow> or C<follow_fast> is specified, for
@@ -187,6 +193,80 @@ Furthermore, deleting or changing files in a symbolically linked directory
might cause very unpleasant surprises, since you delete or change files
in an unknown directory.
+=head1 NOTES
+
+=over 4
+
+=item *
+
+Mac OS (Classic) users should note a few differences:
+
+=over 4
+
+=item *
+
+The path separator is ':', not '/', and the current directory is denoted
+as ':', not '.'. You should be careful about specifying relative pathnames.
+While a full path always begins with a volume name, a relative pathname
+should always begin with a ':'. If specifying a volume name only, a
+trailing ':' is required.
+
+=item *
+
+C<$File::Find::dir> is guaranteed to end with a ':'. If C<$_>
+contains the name of a directory, that name may or may not end with a
+':'. Likewise, C<$File::Find::name>, which contains the complete
+pathname to that directory, and C<$File::Find::fullname>, which holds
+the absolute pathname of that directory with all symbolic links resolved,
+may or may not end with a ':'.
+
+=item *
+
+The default C<untaint_pattern> (see above) on Mac OS is set to
+C<qr|^(.+)$|>. Note that the parentheses are vital.
+
+=item *
+
+The invisible system file "Icon\015" is ignored. While this file may
+appear in every directory, there are some more invisible system files
+on every volume, which are all located at the volume root level (i.e.
+"MacintoshHD:"). These system files are B<not> excluded automatically.
+Your filter may use the following code to recognize invisible files or
+directories (requires Mac::Files):
+
+ use Mac::Files;
+
+ # invisible() -- returns 1 if file/directory is invisible,
+ # 0 if it's visible or undef if an error occured
+
+ sub invisible($) {
+ my $file = shift;
+ my ($fileCat, $fileInfo);
+ my $invisible_flag = 1 << 14;
+
+ if ( $fileCat = FSpGetCatInfo($file) ) {
+ if ($fileInfo = $fileCat->ioFlFndrInfo() ) {
+ return (($fileInfo->fdFlags & $invisible_flag) && 1);
+ }
+ }
+ return undef;
+ }
+
+Generally, invisible files are system files, unless an odd application
+decides to use invisible files for its own purposes. To distinguish
+such files from system files, you have to look at the B<type> and B<creator>
+file attributes. The MacPerl built-in functions C<GetFileInfo(FILE)> and
+C<SetFileInfo(CREATOR, TYPE, FILES)> offer access to these attributes
+(see MacPerl.pm for details).
+
+Files that appear on the desktop actually reside in an (hidden) directory
+named "Desktop Folder" on the particular disk volume. Note that, although
+all desktop files appear to be on the same "virtual" desktop, each disk
+volume actually maintains its own "Desktop Folder" directory.
+
+=back
+
+=back
=cut
@@ -196,8 +276,10 @@ our @EXPORT = qw(find finddepth);
use strict;
my $Is_VMS;
+my $Is_MacOS;
require File::Basename;
+require File::Spec;
my %SLnkSeen;
my ($wanted_callback, $avoid_nlink, $bydepth, $no_chdir, $follow,
@@ -207,7 +289,7 @@ my ($wanted_callback, $avoid_nlink, $bydepth, $no_chdir, $follow,
sub contract_name {
my ($cdir,$fn) = @_;
- return substr($cdir,0,rindex($cdir,'/')) if $fn eq '.';
+ return substr($cdir,0,rindex($cdir,'/')) if $fn eq $File::Find::current_dir;
$cdir = substr($cdir,0,rindex($cdir,'/')+1);
@@ -222,25 +304,83 @@ sub contract_name {
return $abs_name;
}
+# return the absolute name of a directory or file
+sub contract_name_Mac {
+ my ($cdir,$fn) = @_;
+ my $abs_name;
+
+ if ($fn =~ /^(:+)(.*)$/) { # valid pathname starting with a ':'
+
+ my $colon_count = length ($1);
+ if ($colon_count == 1) {
+ $abs_name = $cdir . $2;
+ return $abs_name;
+ }
+ else {
+ # need to move up the tree, but
+ # only if it's not a volume name
+ for (my $i=1; $i<$colon_count; $i++) {
+ unless ($cdir =~ /^[^:]+:$/) { # volume name
+ $cdir =~ s/[^:]+:$//;
+ }
+ else {
+ return undef;
+ }
+ }
+ $abs_name = $cdir . $2;
+ return $abs_name;
+ }
+
+ }
+ else {
+
+ # $fn may be a valid path to a directory or file or (dangling)
+ # symlink, without a leading ':'
+ if ( (-e $fn) || (-l $fn) ) {
+ if ($fn =~ /^[^:]+:/) { # a volume name like DataHD:*
+ return $fn; # $fn is already an absolute path
+ }
+ else {
+ $abs_name = $cdir . $fn;
+ return $abs_name;
+ }
+ }
+ else { # argh!, $fn is not a valid directory/file
+ return undef;
+ }
+ }
+}
sub PathCombine($$) {
my ($Base,$Name) = @_;
my $AbsName;
- if (substr($Name,0,1) eq '/') {
- $AbsName= $Name;
+ if ($Is_MacOS) {
+ # $Name is the resolved symlink (always a full path on MacOS),
+ # i.e. there's no need to call contract_name_Mac()
+ $AbsName = $Name;
+
+ # (simple) check for recursion
+ if ( ( $Base =~ /^$AbsName/) && (-d $AbsName) ) { # recursion
+ return undef;
+ }
}
else {
- $AbsName= contract_name($Base,$Name);
- }
+ if (substr($Name,0,1) eq '/') {
+ $AbsName= $Name;
+ }
+ else {
+ $AbsName= contract_name($Base,$Name);
+ }
- # (simple) check for recursion
- my $newlen= length($AbsName);
- if ($newlen <= length($Base)) {
- if (($newlen == length($Base) || substr($Base,$newlen,1) eq '/')
- && $AbsName eq substr($Base,0,$newlen))
- {
- return undef;
+ # (simple) check for recursion
+ my $newlen= length($AbsName);
+ if ($newlen <= length($Base)) {
+ if (($newlen == length($Base) || substr($Base,$newlen,1) eq '/')
+ && $AbsName eq substr($Base,0,$newlen))
+ {
+ return undef;
+ }
}
}
return $AbsName;
@@ -278,7 +418,7 @@ sub Follow_SymLink($) {
}
if ($full_check && $SLnkSeen{$DEV, $INO}++) {
- if ($follow_skip < 1) {
+ if ( ($follow_skip < 1) || ((-d _) && ($follow_skip < 2)) ) {
die "$AbsName encountered a second time";
}
else {
@@ -293,12 +433,23 @@ our($dir, $name, $fullname, $prune);
sub _find_dir_symlnk($$$);
sub _find_dir($$$);
+# check whether or not a scalar variable is tainted
+# (code straight from the Camel, 3rd ed., page 561)
+sub is_tainted_pp {
+ my $arg = shift;
+ my $nada = substr($arg, 0, 0); # zero-length
+ local $@;
+ eval { eval "# $nada" };
+ return length($@) != 0;
+}
+
sub _find_opt {
my $wanted = shift;
die "invalid top directory" unless defined $_[0];
my $cwd = $wanted->{bydepth} ? Cwd::fastcwd() : Cwd::cwd();
my $cwd_untainted = $cwd;
+ my $check_t_cwd = 1;
$wanted_callback = $wanted->{wanted};
$bydepth = $wanted->{bydepth};
$pre_process = $wanted->{preprocess};
@@ -317,88 +468,125 @@ sub _find_opt {
# a symbolic link to a directory doesn't increase the link count
$avoid_nlink = $follow || $File::Find::dont_use_nlink;
- if ( $untaint ) {
- $cwd_untainted= $1 if $cwd_untainted =~ m|$untaint_pat|;
- die "insecure cwd in find(depth)" unless defined($cwd_untainted);
- }
-
my ($abs_dir, $Is_Dir);
Proc_Top_Item:
foreach my $TOP (@_) {
- my $top_item = $TOP;
- $top_item =~ s|/\z|| unless $top_item eq '/';
- $Is_Dir= 0;
-
- if ($follow) {
- ($topdev,$topino,$topmode,$topnlink) = stat $top_item;
- if (substr($top_item,0,1) eq '/') {
- $abs_dir = $top_item;
- }
- elsif ($top_item eq '.') {
- $abs_dir = $cwd;
- }
- else { # care about any ../
- $abs_dir = contract_name("$cwd/",$top_item);
- }
- $abs_dir= Follow_SymLink($abs_dir);
- unless (defined $abs_dir) {
+ my $top_item = $TOP;
+
+ if ($Is_MacOS) {
+ ($topdev,$topino,$topmode,$topnlink) = $follow ? stat $top_item : lstat $top_item;
+ $top_item = ":$top_item"
+ if ( (-d _) && ($top_item =~ /^[^:]+\z/) );
+ }
+ else {
+ $top_item =~ s|/\z|| unless $top_item eq '/';
+ ($topdev,$topino,$topmode,$topnlink) = $follow ? stat $top_item : lstat $top_item;
+ }
+
+ $Is_Dir= 0;
+
+ if ($follow) {
+
+ if ($Is_MacOS) {
+ $cwd = "$cwd:" unless ($cwd =~ /:$/); # for safety
+
+ if ($top_item eq $File::Find::current_dir) {
+ $abs_dir = $cwd;
+ }
+ else {
+ $abs_dir = contract_name_Mac($cwd, $top_item);
+ unless (defined $abs_dir) {
+ warn "Can't determine absolute path for $top_item (No such file or directory)\n";
+ next Proc_Top_Item;
+ }
+ }
+
+ }
+ else {
+ if (substr($top_item,0,1) eq '/') {
+ $abs_dir = $top_item;
+ }
+ elsif ($top_item eq $File::Find::current_dir) {
+ $abs_dir = $cwd;
+ }
+ else { # care about any ../
+ $abs_dir = contract_name("$cwd/",$top_item);
+ }
+ }
+ $abs_dir= Follow_SymLink($abs_dir);
+ unless (defined $abs_dir) {
warn "$top_item is a dangling symbolic link\n";
next Proc_Top_Item;
- }
- if (-d _) {
+ }
+
+ if (-d _) {
_find_dir_symlnk($wanted, $abs_dir, $top_item);
$Is_Dir= 1;
- }
- }
+ }
+ }
else { # no follow
- ($topdev,$topino,$topmode,$topnlink) = lstat $top_item;
- $topdir = $top_item;
- unless (defined $topnlink) {
- warn "Can't stat $top_item: $!\n";
- next Proc_Top_Item;
- }
- if (-d _) {
+ $topdir = $top_item;
+ unless (defined $topnlink) {
+ warn "Can't stat $top_item: $!\n";
+ next Proc_Top_Item;
+ }
+ if (-d _) {
$top_item =~ s/\.dir\z// if $Is_VMS;
_find_dir($wanted, $top_item, $topnlink);
$Is_Dir= 1;
- }
+ }
else {
$abs_dir= $top_item;
- }
- }
+ }
+ }
- unless ($Is_Dir) {
+ unless ($Is_Dir) {
unless (($_,$dir) = File::Basename::fileparse($abs_dir)) {
- ($dir,$_) = ('./', $top_item);
+ if ($Is_MacOS) {
+ ($dir,$_) = (':', $top_item); # $File::Find::dir, $_
+ }
+ else {
+ ($dir,$_) = ('./', $top_item);
+ }
}
- $abs_dir = $dir;
- if ($untaint) {
- my $abs_dir_save = $abs_dir;
- $abs_dir = $1 if $abs_dir =~ m|$untaint_pat|;
+ $abs_dir = $dir;
+ if (( $untaint ) && (is_tainted($dir) )) {
+ ( $abs_dir ) = $dir =~ m|$untaint_pat|;
unless (defined $abs_dir) {
if ($untaint_skip == 0) {
- die "directory $abs_dir_save is still tainted";
+ die "directory $dir is still tainted";
}
else {
next Proc_Top_Item;
}
}
- }
+ }
- unless ($no_chdir or chdir $abs_dir) {
- warn "Couldn't chdir $abs_dir: $!\n";
- next Proc_Top_Item;
- }
+ unless ($no_chdir || chdir $abs_dir) {
+ warn "Couldn't chdir $abs_dir: $!\n";
+ next Proc_Top_Item;
+ }
- $name = $abs_dir . $_;
+ $name = $abs_dir . $_; # $File::Find::name
- { &$wanted_callback }; # protect against wild "next"
+ { &$wanted_callback }; # protect against wild "next"
- }
+ }
- $no_chdir or chdir $cwd_untainted;
+ unless ( $no_chdir ) {
+ if ( ($check_t_cwd) && (($untaint) && (is_tainted($cwd) )) ) {
+ ( $cwd_untainted ) = $cwd =~ m|$untaint_pat|;
+ unless (defined $cwd_untainted) {
+ die "insecure cwd in find(depth)";
+ }
+ $check_t_cwd = 0;
+ }
+ unless (chdir $cwd_untainted) {
+ die "Can't cd to $cwd: $!\n";
+ }
+ }
}
}
@@ -417,15 +605,25 @@ sub _find_dir($$$) {
my ($subcount,$sub_nlink);
my $SE= [];
my $dir_name= $p_dir;
- my $dir_pref= ( $p_dir eq '/' ? '/' : "$p_dir/" );
- my $dir_rel= '.'; # directory name relative to current directory
+ my $dir_pref;
+ my $dir_rel;
+ my $tainted = 0;
+
+ if ($Is_MacOS) {
+ $dir_pref= ($p_dir =~ /:$/) ? $p_dir : "$p_dir:"; # preface
+ $dir_rel= ':'; # directory name relative to current directory
+ }
+ else {
+ $dir_pref= ( $p_dir eq '/' ? '/' : "$p_dir/" );
+ $dir_rel= '.'; # directory name relative to current directory
+ }
local ($dir, $name, $prune, *DIR);
-
- unless ($no_chdir or $p_dir eq '.') {
+
+ unless ( $no_chdir || ($p_dir eq $File::Find::current_dir)) {
my $udir = $p_dir;
- if ($untaint) {
- $udir = $1 if $p_dir =~ m|$untaint_pat|;
+ if (( $untaint ) && (is_tainted($p_dir) )) {
+ ( $udir ) = $p_dir =~ m|$untaint_pat|;
unless (defined $udir) {
if ($untaint_skip == 0) {
die "directory $p_dir is still tainted";
@@ -440,46 +638,63 @@ sub _find_dir($$$) {
return;
}
}
-
+
+ # push the starting directory
push @Stack,[$CdLvl,$p_dir,$dir_rel,-1] if $bydepth;
+ if ($Is_MacOS) {
+ $p_dir = $dir_pref; # ensure trailing ':'
+ }
+
while (defined $SE) {
unless ($bydepth) {
- $dir= $p_dir;
- $name= $dir_name;
- $_= ($no_chdir ? $dir_name : $dir_rel );
+ $dir= $p_dir; # $File::Find::dir
+ $name= $dir_name; # $File::Find::name
+ $_= ($no_chdir ? $dir_name : $dir_rel ); # $_
# prune may happen here
- $prune= 0;
- { &$wanted_callback }; # protect against wild "next"
- next if $prune;
+ $prune= 0;
+ { &$wanted_callback }; # protect against wild "next"
+ next if $prune;
}
-
+
# change to that directory
- unless ($no_chdir or $dir_rel eq '.') {
+ unless ($no_chdir || ($dir_rel eq $File::Find::current_dir)) {
my $udir= $dir_rel;
- if ($untaint) {
- $udir = $1 if $dir_rel =~ m|$untaint_pat|;
+ if ( ($untaint) && (($tainted) || ($tainted = is_tainted($dir_rel) )) ) {
+ ( $udir ) = $dir_rel =~ m|$untaint_pat|;
unless (defined $udir) {
if ($untaint_skip == 0) {
- die "directory ("
- . ($p_dir ne '/' ? $p_dir : '')
- . "/) $dir_rel is still tainted";
+ if ($Is_MacOS) {
+ die "directory ($p_dir) $dir_rel is still tainted";
+ }
+ else {
+ die "directory (" . ($p_dir ne '/' ? $p_dir : '') . "/) $dir_rel is still tainted";
+ }
+ } else { # $untaint_skip == 1
+ next;
}
}
}
unless (chdir $udir) {
- warn "Can't cd to ("
- . ($p_dir ne '/' ? $p_dir : '')
- . "/) $udir : $!\n";
+ if ($Is_MacOS) {
+ warn "Can't cd to ($p_dir) $udir: $!\n";
+ }
+ else {
+ warn "Can't cd to (" . ($p_dir ne '/' ? $p_dir : '') . "/) $udir: $!\n";
+ }
next;
}
$CdLvl++;
}
- $dir= $dir_name;
+ if ($Is_MacOS) {
+ $dir_name = "$dir_name:" unless ($dir_name =~ /:$/);
+ }
+
+ $dir= $dir_name; # $File::Find::dir
# Get the list of files in the current directory.
- unless (opendir DIR, ($no_chdir ? $dir_name : '.')) {
+ unless (opendir DIR, ($no_chdir ? $dir_name : $File::Find::current_dir)) {
warn "Can't opendir($dir_name): $!\n";
next;
}
@@ -491,10 +706,10 @@ sub _find_dir($$$) {
if ($nlink == 2 && !$avoid_nlink) {
# This dir has no subdirectories.
for my $FN (@filenames) {
- next if $FN =~ /^\.{1,2}\z/;
+ next if $FN =~ $File::Find::skip_pattern;
- $name = $dir_pref . $FN;
- $_ = ($no_chdir ? $name : $FN);
+ $name = $dir_pref . $FN; # $File::Find::name
+ $_ = ($no_chdir ? $name : $FN); # $_
{ &$wanted_callback }; # protect against wild "next"
}
@@ -504,7 +719,7 @@ sub _find_dir($$$) {
$subcount = $nlink - 2;
for my $FN (@filenames) {
- next if $FN =~ /^\.{1,2}\z/;
+ next if $FN =~ $File::Find::skip_pattern;
if ($subcount > 0 || $avoid_nlink) {
# Seen all the subdirs?
# check for directoriness.
@@ -517,14 +732,14 @@ sub _find_dir($$$) {
push @Stack,[$CdLvl,$dir_name,$FN,$sub_nlink];
}
else {
- $name = $dir_pref . $FN;
- $_= ($no_chdir ? $name : $FN);
+ $name = $dir_pref . $FN; # $File::Find::name
+ $_= ($no_chdir ? $name : $FN); # $_
{ &$wanted_callback }; # protect against wild "next"
}
}
else {
- $name = $dir_pref . $FN;
- $_= ($no_chdir ? $name : $FN);
+ $name = $dir_pref . $FN; # $File::Find::name
+ $_= ($no_chdir ? $name : $FN); # $_
{ &$wanted_callback }; # protect against wild "next"
}
}
@@ -534,32 +749,65 @@ sub _find_dir($$$) {
while ( defined ($SE = pop @Stack) ) {
($Level, $p_dir, $dir_rel, $nlink) = @$SE;
if ($CdLvl > $Level && !$no_chdir) {
- my $tmp = join('/',('..') x ($CdLvl-$Level));
- die "Can't cd to $dir_name" . $tmp
- unless chdir ($tmp);
+ my $tmp;
+ if ($Is_MacOS) {
+ $tmp = (':' x ($CdLvl-$Level)) . ':';
+ }
+ else {
+ $tmp = join('/',('..') x ($CdLvl-$Level));
+ }
+ die "Can't cd to $dir_name" . $tmp
+ unless chdir ($tmp);
$CdLvl = $Level;
}
- $dir_name = ($p_dir eq '/' ? "/$dir_rel" : "$p_dir/$dir_rel");
- $dir_pref = "$dir_name/";
+
+ if ($Is_MacOS) {
+ # $pdir always has a trailing ':', except for the starting dir,
+ # where $dir_rel eq ':'
+ $dir_name = "$p_dir$dir_rel";
+ $dir_pref = "$dir_name:";
+ }
+ else {
+ $dir_name = ($p_dir eq '/' ? "/$dir_rel" : "$p_dir/$dir_rel");
+ $dir_pref = "$dir_name/";
+ }
+
if ( $nlink == -2 ) {
- $name = $dir = $p_dir;
- $_ = ".";
+ $name = $dir = $p_dir; # $File::Find::name / dir
+ if ($Is_MacOS) {
+ $_ = ':'; # $_
+ }
+ else {
+ $_ = '.';
+ }
&$post_process; # End-of-directory processing
- } elsif ( $nlink < 0 ) { # must be finddepth, report dirname now
- $name = $dir_name;
- if ( substr($name,-2) eq '/.' ) {
- $name =~ s|/\.$||;
- }
- $dir = $p_dir;
- $_ = ($no_chdir ? $dir_name : $dir_rel );
- if ( substr($_,-2) eq '/.' ) {
- s|/\.$||;
- }
- { &$wanted_callback }; # protect against wild "next"
- } else {
- push @Stack,[$CdLvl,$p_dir,$dir_rel,-1] if $bydepth;
- last;
- }
+ }
+ elsif ( $nlink < 0 ) { # must be finddepth, report dirname now
+ $name = $dir_name;
+ if ($Is_MacOS) {
+ if ($dir_rel eq ':') { # must be the top dir, where we started
+ $name =~ s|:$||; # $File::Find::name
+ $p_dir = "$p_dir:" unless ($p_dir =~ /:$/);
+ }
+ $dir = $p_dir; # $File::Find::dir
+ $_ = ($no_chdir ? $name : $dir_rel); # $_
+ }
+ else {
+ if ( substr($name,-2) eq '/.' ) {
+ $name =~ s|/\.$||;
+ }
+ $dir = $p_dir;
+ $_ = ($no_chdir ? $dir_name : $dir_rel );
+ if ( substr($_,-2) eq '/.' ) {
+ s|/\.$||;
+ }
+ }
+ { &$wanted_callback }; # protect against wild "next"
+ }
+ else {
+ push @Stack,[$CdLvl,$p_dir,$dir_rel,-1] if $bydepth;
+ last;
+ }
}
}
}
@@ -573,25 +821,40 @@ sub _find_dir($$$) {
# chdir (if not no_chdir) to dir
sub _find_dir_symlnk($$$) {
- my ($wanted, $dir_loc, $p_dir) = @_;
+ my ($wanted, $dir_loc, $p_dir) = @_; # $dir_loc is the absolute directory
my @Stack;
my @filenames;
my $new_loc;
- my $pdir_loc = $dir_loc;
+ my $updir_loc = $dir_loc; # untainted parent directory
my $SE = [];
my $dir_name = $p_dir;
- my $dir_pref = ( $p_dir eq '/' ? '/' : "$p_dir/" );
- my $loc_pref = ( $dir_loc eq '/' ? '/' : "$dir_loc/" );
- my $dir_rel = '.'; # directory name relative to current directory
- my $byd_flag; # flag for pending stack entry if $bydepth
+ my $dir_pref;
+ my $loc_pref;
+ my $dir_rel;
+ my $byd_flag; # flag for pending stack entry if $bydepth
+ my $tainted = 0;
+ my $ok = 1;
+
+ if ($Is_MacOS) {
+ $dir_pref = ($p_dir =~ /:$/) ? "$p_dir" : "$p_dir:";
+ $loc_pref = ($dir_loc =~ /:$/) ? "$dir_loc" : "$dir_loc:";
+ $dir_rel = ':'; # directory name relative to current directory
+ } else {
+ $dir_pref = ( $p_dir eq '/' ? '/' : "$p_dir/" );
+ $loc_pref = ( $dir_loc eq '/' ? '/' : "$dir_loc/" );
+ $dir_rel = '.'; # directory name relative to current directory
+ }
local ($dir, $name, $fullname, $prune, *DIR);
-
- unless ($no_chdir or $p_dir eq '.') {
- my $udir = $dir_loc;
- if ($untaint) {
- $udir = $1 if $dir_loc =~ m|$untaint_pat|;
- unless (defined $udir) {
+
+ unless ($no_chdir) {
+ # untaint the topdir
+ if (( $untaint ) && (is_tainted($dir_loc) )) {
+ ( $updir_loc ) = $dir_loc =~ m|$untaint_pat|; # parent dir, now untainted
+ # once untainted, $updir_loc is pushed on the stack (as parent directory);
+ # hence, we don't need to untaint the parent directory every time we chdir
+ # to it later
+ unless (defined $updir_loc) {
if ($untaint_skip == 0) {
die "directory $dir_loc is still tainted";
}
@@ -600,45 +863,47 @@ sub _find_dir_symlnk($$$) {
}
}
}
- unless (chdir $udir) {
- warn "Can't cd to $udir: $!\n";
+ $ok = chdir($updir_loc) unless ($p_dir eq $File::Find::current_dir);
+ unless ($ok) {
+ warn "Can't cd to $updir_loc: $!\n";
return;
}
}
- push @Stack,[$dir_loc,$pdir_loc,$p_dir,$dir_rel,-1] if $bydepth;
+ push @Stack,[$dir_loc,$updir_loc,$p_dir,$dir_rel,-1] if $bydepth;
+
+ if ($Is_MacOS) {
+ $p_dir = $dir_pref; # ensure trailing ':'
+ }
while (defined $SE) {
unless ($bydepth) {
- # change to parent directory
+ # change (back) to parent directory (always untainted)
unless ($no_chdir) {
- my $udir = $pdir_loc;
- if ($untaint) {
- $udir = $1 if $pdir_loc =~ m|$untaint_pat|;
- }
- unless (chdir $udir) {
- warn "Can't cd to $udir: $!\n";
+ unless (chdir $updir_loc) {
+ warn "Can't cd to $updir_loc: $!\n";
next;
}
}
- $dir= $p_dir;
- $name= $dir_name;
- $_= ($no_chdir ? $dir_name : $dir_rel );
- $fullname= $dir_loc;
+ $dir= $p_dir; # $File::Find::dir
+ $name= $dir_name; # $File::Find::name
+ $_= ($no_chdir ? $dir_name : $dir_rel ); # $_
+ $fullname= $dir_loc; # $File::Find::fullname
# prune may happen here
- $prune= 0;
+ $prune= 0;
lstat($_); # make sure file tests with '_' work
- { &$wanted_callback }; # protect against wild "next"
- next if $prune;
+ { &$wanted_callback }; # protect against wild "next"
+ next if $prune;
}
# change to that directory
- unless ($no_chdir or $dir_rel eq '.') {
- my $udir = $dir_loc;
- if ($untaint) {
- $udir = $1 if $dir_loc =~ m|$untaint_pat|;
- unless (defined $udir ) {
+ unless ($no_chdir || ($dir_rel eq $File::Find::current_dir)) {
+ $updir_loc = $dir_loc;
+ if ( ($untaint) && (($tainted) || ($tainted = is_tainted($dir_loc) )) ) {
+ # untaint $dir_loc, what will be pushed on the stack as (untainted) parent dir
+ ( $updir_loc ) = $dir_loc =~ m|$untaint_pat|;
+ unless (defined $updir_loc) {
if ($untaint_skip == 0) {
die "directory $dir_loc is still tainted";
}
@@ -647,16 +912,20 @@ sub _find_dir_symlnk($$$) {
}
}
}
- unless (chdir $udir) {
- warn "Can't cd to $udir: $!\n";
+ unless (chdir $updir_loc) {
+ warn "Can't cd to $updir_loc: $!\n";
next;
}
}
- $dir = $dir_name;
+ if ($Is_MacOS) {
+ $dir_name = "$dir_name:" unless ($dir_name =~ /:$/);
+ }
+
+ $dir = $dir_name; # $File::Find::dir
# Get the list of files in the current directory.
- unless (opendir DIR, ($no_chdir ? $dir_loc : '.')) {
+ unless (opendir DIR, ($no_chdir ? $dir_loc : $File::Find::current_dir)) {
warn "Can't opendir($dir_loc): $!\n";
next;
}
@@ -664,21 +933,21 @@ sub _find_dir_symlnk($$$) {
closedir(DIR);
for my $FN (@filenames) {
- next if $FN =~ /^\.{1,2}\z/;
+ next if $FN =~ $File::Find::skip_pattern;
# follow symbolic links / do an lstat
$new_loc = Follow_SymLink($loc_pref.$FN);
# ignore if invalid symlink
next unless defined $new_loc;
-
+
if (-d _) {
- push @Stack,[$new_loc,$dir_loc,$dir_name,$FN,1];
+ push @Stack,[$new_loc,$updir_loc,$dir_name,$FN,1];
}
else {
- $fullname = $new_loc;
- $name = $dir_pref . $FN;
- $_ = ($no_chdir ? $name : $FN);
+ $fullname = $new_loc; # $File::Find::fullname
+ $name = $dir_pref . $FN; # $File::Find::name
+ $_ = ($no_chdir ? $name : $FN); # $_
{ &$wanted_callback }; # protect against wild "next"
}
}
@@ -686,38 +955,54 @@ sub _find_dir_symlnk($$$) {
}
continue {
while (defined($SE = pop @Stack)) {
- ($dir_loc, $pdir_loc, $p_dir, $dir_rel, $byd_flag) = @$SE;
- $dir_name = ($p_dir eq '/' ? "/$dir_rel" : "$p_dir/$dir_rel");
- $dir_pref = "$dir_name/";
- $loc_pref = "$dir_loc/";
- if ( $byd_flag < 0 ) { # must be finddepth, report dirname now
- unless ($no_chdir or $dir_rel eq '.') {
- my $udir = $pdir_loc;
- if ($untaint) {
- $udir = $1 if $dir_loc =~ m|$untaint_pat|;
- }
- unless (chdir $udir) {
- warn "Can't cd to $udir: $!\n";
- next;
- }
- }
- $fullname = $dir_loc;
- $name = $dir_name;
- if ( substr($name,-2) eq '/.' ) {
- $name =~ s|/\.$||;
- }
- $dir = $p_dir;
- $_ = ($no_chdir ? $dir_name : $dir_rel);
- if ( substr($_,-2) eq '/.' ) {
- s|/\.$||;
- }
-
- lstat($_); # make sure file tests with '_' work
- { &$wanted_callback }; # protect against wild "next"
- } else {
- push @Stack,[$dir_loc, $pdir_loc, $p_dir, $dir_rel,-1] if $bydepth;
- last;
- }
+ ($dir_loc, $updir_loc, $p_dir, $dir_rel, $byd_flag) = @$SE;
+ if ($Is_MacOS) {
+ # $p_dir always has a trailing ':', except for the starting dir,
+ # where $dir_rel eq ':'
+ $dir_name = "$p_dir$dir_rel";
+ $dir_pref = "$dir_name:";
+ $loc_pref = ($dir_loc =~ /:$/) ? $dir_loc : "$dir_loc:";
+ }
+ else {
+ $dir_name = ($p_dir eq '/' ? "/$dir_rel" : "$p_dir/$dir_rel");
+ $dir_pref = "$dir_name/";
+ $loc_pref = "$dir_loc/";
+ }
+ if ( $byd_flag < 0 ) { # must be finddepth, report dirname now
+ unless ($no_chdir || ($dir_rel eq $File::Find::current_dir)) {
+ unless (chdir $updir_loc) { # $updir_loc (parent dir) is always untainted
+ warn "Can't cd to $updir_loc: $!\n";
+ next;
+ }
+ }
+ $fullname = $dir_loc; # $File::Find::fullname
+ $name = $dir_name; # $File::Find::name
+ if ($Is_MacOS) {
+ if ($dir_rel eq ':') { # must be the top dir, where we started
+ $name =~ s|:$||; # $File::Find::name
+ $p_dir = "$p_dir:" unless ($p_dir =~ /:$/);
+ }
+ $dir = $p_dir; # $File::Find::dir
+ $_ = ($no_chdir ? $name : $dir_rel); # $_
+ }
+ else {
+ if ( substr($name,-2) eq '/.' ) {
+ $name =~ s|/\.$||; # $File::Find::name
+ }
+ $dir = $p_dir; # $File::Find::dir
+ $_ = ($no_chdir ? $dir_name : $dir_rel); # $_
+ if ( substr($_,-2) eq '/.' ) {
+ s|/\.$||;
+ }
+ }
+
+ lstat($_); # make sure file tests with '_' work
+ { &$wanted_callback }; # protect against wild "next"
+ }
+ else {
+ push @Stack,[$dir_loc, $updir_loc, $p_dir, $dir_rel,-1] if $bydepth;
+ last;
+ }
}
}
}
@@ -730,7 +1015,7 @@ sub wrap_wanted {
$wanted->{follow_skip} = 1 unless defined $wanted->{follow_skip};
}
if ( $wanted->{untaint} ) {
- $wanted->{untaint_pattern} = qr|^([-+@\w./]+)$|
+ $wanted->{untaint_pattern} = $File::Find::untaint_pattern
unless defined $wanted->{untaint_pattern};
$wanted->{untaint_skip} = 0 unless defined $wanted->{untaint_skip};
}
@@ -743,23 +1028,39 @@ sub wrap_wanted {
sub find {
my $wanted = shift;
+ %SLnkSeen= (); # clear hash first
_find_opt(wrap_wanted($wanted), @_);
%SLnkSeen= (); # free memory
}
sub finddepth {
my $wanted = wrap_wanted(shift);
+ %SLnkSeen= (); # clear hash first
$wanted->{bydepth} = 1;
_find_opt($wanted, @_);
%SLnkSeen= (); # free memory
}
+# default
+$File::Find::skip_pattern = qr/^\.{1,2}\z/;
+$File::Find::untaint_pattern = qr|^([-+@\w./]+)$|;
+
# These are hard-coded for now, but may move to hint files.
if ($^O eq 'VMS') {
$Is_VMS = 1;
- $File::Find::dont_use_nlink = 1;
+ $File::Find::dont_use_nlink = 1;
+}
+elsif ($^O eq 'MacOS') {
+ $Is_MacOS = 1;
+ $File::Find::dont_use_nlink = 1;
+ $File::Find::skip_pattern = qr/^Icon\015\z/;
+ $File::Find::untaint_pattern = qr|^(.+)$|;
}
+# this _should_ work properly on all platforms
+# where File::Find can be expected to work
+$File::Find::current_dir = File::Spec->curdir || '.';
+
$File::Find::dont_use_nlink = 1
if $^O eq 'os2' || $^O eq 'dos' || $^O eq 'amigaos' || $^O eq 'MSWin32' ||
$^O eq 'cygwin' || $^O eq 'epoc';
@@ -773,4 +1074,13 @@ unless ($File::Find::dont_use_nlink) {
$File::Find::dont_use_nlink = 1 if ($Config::Config{'dont_use_nlink'});
}
+# We need a function that checks if a scalar is tainted. Either use the
+# Scalar::Util module's tainted() function or our (slower) pure Perl
+# fallback is_tainted_pp()
+{
+ local $@;
+ eval { require Scalar::Util };
+ *is_tainted = $@ ? \&is_tainted_pp : \&Scalar::Util::tainted;
+}
+
1;
diff --git a/t/lib/filefind.t b/t/lib/filefind.t
index 72e2669ad0..1152cdf157 100755
--- a/t/lib/filefind.t
+++ b/t/lib/filefind.t
@@ -1,43 +1,79 @@
-####!./perl
+#!./perl -T
my %Expect;
my $symlink_exists = eval { symlink("",""); 1 };
+my $warn_msg;
+my $cwd;
+my $cwd_untainted;
BEGIN {
chdir 't' if -d 't';
- @INC = '../lib';
+ @INC = '../lib';
+
+ for (keys %ENV) { # untaint ENV
+ ($ENV{$_}) = keys %{{ map {$_ => 1} $ENV{$_} }};
+ }
+
+ $SIG{'__WARN__'} = sub { $warn_msg = $_[0]; warn "# Warn: $_[0]"; }
}
-if ( $symlink_exists ) { print "1..119\n"; }
-else { print "1..61\n"; }
+if ( $symlink_exists ) { print "1..184\n"; }
+else { print "1..75\n"; }
use File::Find;
+use Cwd;
cleanup();
-find(sub { print "ok 1\n" if $_ eq 'filefind.t'; }, ".");
-finddepth(sub { print "ok 2\n" if $_ eq 'filefind.t'; }, ".");
+if ($^O eq 'MacOS') {
+ find({wanted => sub { print "ok 1\n" if $_ eq 'filefind.t'; }, untaint => 1}, ':');
+ finddepth({wanted => sub { print "ok 2\n" if $_ eq 'filefind.t'; }, untaint => 1}, ':');
+} else {
+ find({wanted => sub { print "ok 1\n" if $_ eq 'filefind.t'; }, untaint => 1,
+ untaint_pattern => qr|^(.+)$|}, '.');
+ finddepth({wanted => sub { print "ok 2\n" if $_ eq 'filefind.t'; },
+ untaint => 1, untaint_pattern => qr|^(.+)$|}, '.');
+}
my $case = 2;
my $FastFileTests_OK = 0;
sub cleanup {
- if (-d 'for_find') {
- chdir('for_find');
- }
- if (-d 'fa') {
- unlink 'fa/fa_ord', 'fa/fsl', 'fa/faa/faa_ord',
- 'fa/fab/fab_ord', 'fa/fab/faba/faba_ord',
- 'fb/fb_ord', 'fb/fba/fba_ord';
- rmdir 'fa/faa';
- rmdir 'fa/fab/faba';
- rmdir 'fa/fab';
- rmdir 'fa';
- rmdir 'fb/fba';
- rmdir 'fb';
- chdir '..';
- rmdir 'for_find';
+ if ($^O eq 'MacOS') {
+ if (-d ':for_find') {
+ chdir(':for_find');
+ }
+ if (-d ':fa') {
+ unlink ':fa:fa_ord',':fa:fsl',':fa:faa:faa_ord',
+ ':fa:fab:fab_ord',':fa:fab:faba:faba_ord',
+ ':fb:fb_ord',':fb:fba:fba_ord';
+ rmdir ':fa:faa';
+ rmdir ':fa:fab:faba';
+ rmdir ':fa:fab';
+ rmdir ':fa';
+ rmdir ':fb:fba';
+ rmdir ':fb';
+ chdir '::';
+ rmdir ':for_find';
+ }
+ } else {
+ if (-d 'for_find') {
+ chdir('for_find');
+ }
+ if (-d 'fa') {
+ unlink 'fa/fa_ord','fa/fsl','fa/faa/faa_ord',
+ 'fa/fab/fab_ord','fa/fab/faba/faba_ord',
+ 'fb/fb_ord','fb/fba/fba_ord';
+ rmdir 'fa/faa';
+ rmdir 'fa/fab/faba';
+ rmdir 'fa/fab';
+ rmdir 'fa';
+ rmdir 'fb/fba';
+ rmdir 'fb';
+ chdir '..';
+ rmdir 'for_find';
+ }
}
}
@@ -66,7 +102,7 @@ sub MkDir($$) {
}
sub wanted {
- print "# '$_' => 1\n";
+ print "# '$_' => 1\n";
s#\.$## if ($^O eq 'VMS' && $_ ne '.');
Check( $Expect{$_} );
if ( $FastFileTests_OK ) {
@@ -77,7 +113,7 @@ sub wanted {
unless ( $Expect_Dir{$_} && ! -d $_ );
}
$File::Find::prune=1 if $_ eq 'faba';
-
+
}
sub dn_wanted {
@@ -86,8 +122,10 @@ sub dn_wanted {
print "# '$n' => 1\n";
my $i = rindex($n,'/');
my $OK = exists($Expect{$n});
- if ( $OK ) {
- $OK= exists($Expect{substr($n,0,$i)}) if $i >= 0;
+ unless ($^O eq 'MacOS') {
+ if ( $OK ) {
+ $OK= exists($Expect{substr($n,0,$i)}) if $i >= 0;
+ }
}
Check($OK);
delete $Expect{$n};
@@ -98,120 +136,551 @@ sub d_wanted {
s#\.$## if ($^O eq 'VMS' && $_ ne '.');
my $i = rindex($_,'/');
my $OK = exists($Expect{$_});
- if ( $OK ) {
- $OK= exists($Expect{substr($_,0,$i)}) if $i >= 0;
+ unless ($^O eq 'MacOS') {
+ if ( $OK ) {
+ $OK= exists($Expect{substr($_,0,$i)}) if $i >= 0;
+ }
}
Check($OK);
delete $Expect{$_};
}
-MkDir( 'for_find',0770 );
-CheckDie(chdir(for_find));
-MkDir( 'fa',0770 );
-MkDir( 'fb',0770 );
-touch('fb/fb_ord');
-MkDir( 'fb/fba',0770 );
-touch('fb/fba/fba_ord');
-CheckDie( symlink('../fb','fa/fsl') ) if $symlink_exists;
-touch('fa/fa_ord');
-
-MkDir( 'fa/faa',0770 );
-touch('fa/faa/faa_ord');
-MkDir( 'fa/fab',0770 );
-touch('fa/fab/fab_ord');
-MkDir( 'fa/fab/faba',0770 );
-touch('fa/fab/faba/faba_ord');
-
-%Expect = ('.' => 1, 'fsl' => 1, 'fa_ord' => 1, 'fab' => 1, 'fab_ord' => 1,
- 'faba' => 1, 'faa' => 1, 'faa_ord' => 1);
-delete $Expect{'fsl'} unless $symlink_exists;
-%Expect_Dir = ('fa' => 1, 'faa' => 1, 'fab' => 1, 'faba' => 1,
- 'fb' => 1, 'fba' => 1);
-delete @Expect_Dir{'fb','fba'} unless $symlink_exists;
-File::Find::find( {wanted => \&wanted, },'fa' );
-Check( scalar(keys %Expect) == 0 );
-
-%Expect=('fa' => 1, 'fa/fsl' => 1, 'fa/fa_ord' => 1, 'fa/fab' => 1,
- 'fa/fab/fab_ord' => 1, 'fa/fab/faba' => 1,
- 'fa/fab/faba/faba_ord' => 1, 'fa/faa' => 1, 'fa/faa/faa_ord' => 1);
-delete $Expect{'fa/fsl'} unless $symlink_exists;
-%Expect_Dir = ('fa' => 1, 'fa/faa' => 1, '/fa/fab' => 1, 'fa/fab/faba' => 1,
- 'fb' => 1, 'fb/fba' => 1);
-delete @Expect_Dir{'fb','fb/fba'} unless $symlink_exists;
-File::Find::find( {wanted => \&wanted, no_chdir => 1},'fa' );
-
-Check( scalar(keys %Expect) == 0 );
-
-%Expect=('.' => 1, './fa' => 1, './fa/fsl' => 1, './fa/fa_ord' => 1, './fa/fab' => 1,
- './fa/fab/fab_ord' => 1, './fa/fab/faba' => 1,
- './fa/fab/faba/faba_ord' => 1, './fa/faa' => 1, './fa/faa/faa_ord' => 1,
- './fb' => 1, './fb/fba' => 1, './fb/fba/fba_ord' => 1, './fb/fb_ord' => 1);
-delete $Expect{'./fa/fsl'} unless $symlink_exists;
-%Expect_Dir = ('./fa' => 1, './fa/faa' => 1, '/fa/fab' => 1, './fa/fab/faba' => 1,
- './fb' => 1, './fb/fba' => 1);
-delete @Expect_Dir{'./fb','./fb/fba'} unless $symlink_exists;
-File::Find::finddepth( {wanted => \&dn_wanted },'.' );
-Check( scalar(keys %Expect) == 0 );
-
-%Expect=('.' => 1, './fa' => 1, './fa/fsl' => 1, './fa/fa_ord' => 1, './fa/fab' => 1,
- './fa/fab/fab_ord' => 1, './fa/fab/faba' => 1,
- './fa/fab/faba/faba_ord' => 1, './fa/faa' => 1, './fa/faa/faa_ord' => 1,
- './fb' => 1, './fb/fba' => 1, './fb/fba/fba_ord' => 1, './fb/fb_ord' => 1);
-delete $Expect{'./fa/fsl'} unless $symlink_exists;
-%Expect_Dir = ('./fa' => 1, './fa/faa' => 1, '/fa/fab' => 1, './fa/fab/faba' => 1,
- './fb' => 1, './fb/fba' => 1);
-delete @Expect_Dir{'./fb','./fb/fba'} unless $symlink_exists;
-File::Find::finddepth( {wanted => \&d_wanted, no_chdir => 1 },'.' );
-Check( scalar(keys %Expect) == 0 );
-
-if ( $symlink_exists ) {
- $FastFileTests_OK= 1;
- %Expect=('.' => 1, 'fa_ord' => 1, 'fsl' => 1, 'fb_ord' => 1, 'fba' => 1,
- 'fba_ord' => 1, 'fab' => 1, 'fab_ord' => 1, 'faba' => 1, 'faa' => 1,
- 'faa_ord' => 1);
- %Expect_Dir = ('fa' => 1, 'fa/faa' => 1, '/fa/fab' => 1, 'fa/fab/faba' => 1,
- 'fb' => 1, 'fb/fba' => 1);
-
- File::Find::find( {wanted => \&wanted, follow_fast => 1},'fa' );
- Check( scalar(keys %Expect) == 0 );
-
- %Expect=('fa' => 1, 'fa/fa_ord' => 1, 'fa/fsl' => 1, 'fa/fsl/fb_ord' => 1,
- 'fa/fsl/fba' => 1, 'fa/fsl/fba/fba_ord' => 1, 'fa/fab' => 1,
- 'fa/fab/fab_ord' => 1, 'fa/fab/faba' => 1, 'fa/fab/faba/faba_ord' => 1,
- 'fa/faa' => 1, 'fa/faa/faa_ord' => 1);
- %Expect_Dir = ('fa' => 1, 'fa/faa' => 1, '/fa/fab' => 1, 'fa/fab/faba' => 1,
- 'fb' => 1, 'fb/fba' => 1);
- File::Find::find( {wanted => \&wanted, follow_fast => 1, no_chdir => 1},'fa' );
- Check( scalar(keys %Expect) == 0 );
-
- %Expect=('fa' => 1, 'fa/fa_ord' => 1, 'fa/fsl' => 1, 'fa/fsl/fb_ord' => 1,
- 'fa/fsl/fba' => 1, 'fa/fsl/fba/fba_ord' => 1, 'fa/fab' => 1,
- 'fa/fab/fab_ord' => 1, 'fa/fab/faba' => 1, 'fa/fab/faba/faba_ord' => 1,
- 'fa/faa' => 1, 'fa/faa/faa_ord' => 1);
- %Expect_Dir = ('fa' => 1, 'fa/faa' => 1, '/fa/fab' => 1, 'fa/fab/faba' => 1,
- 'fb' => 1, 'fb/fba' => 1);
-
- File::Find::finddepth( {wanted => \&dn_wanted, follow_fast => 1},'fa' );
- Check( scalar(keys %Expect) == 0 );
-
- %Expect=('fa' => 1, 'fa/fa_ord' => 1, 'fa/fsl' => 1, 'fa/fsl/fb_ord' => 1,
- 'fa/fsl/fba' => 1, 'fa/fsl/fba/fba_ord' => 1, 'fa/fab' => 1,
- 'fa/fab/fab_ord' => 1, 'fa/fab/faba' => 1, 'fa/fab/faba/faba_ord' => 1,
- 'fa/faa' => 1, 'fa/faa/faa_ord' => 1);
- %Expect_Dir = ('fa' => 1, 'fa/faa' => 1, '/fa/fab' => 1, 'fa/fab/faba' => 1,
- 'fb' => 1, 'fb/fba' => 1);
-
- File::Find::finddepth( {wanted => \&d_wanted, follow_fast => 1, no_chdir => 1},'fa' );
- Check( scalar(keys %Expect) == 0 );
-
- # Verify that File::Find::find will call wanted even if the topdir of
- # is a symlink to a directory, and it shouldn't follow the link
- # unless follow is set, which it isn't in this case
- %Expect = ('fsl' => 1);
- %Expect_Dir = ();
- File::Find::find( {wanted => \&wanted, },'fa/fsl' );
- Check( scalar(keys %Expect) == 0 );
+sub simple_wanted {
+ print "# \$File::Find::dir => '$File::Find::dir'\n";
+ print "# \$_ => '$_'\n";
+}
+
+sub noop_wanted {}
+sub my_preprocess {
+ @files = @_;
+ print "# --PREPROCESS--\n";
+ print "# \$File::Find::dir => '$File::Find::dir' \n";
+ foreach $file (@files) {
+ print "# $file \n";
+ delete $Expect{$File::Find::dir}->{$file};
+ }
+ print "# --END PREPROCESS--\n";
+ Check(scalar(keys %{$Expect{$File::Find::dir}}) == 0);
+ if (scalar(keys %{$Expect{$File::Find::dir}}) == 0) {
+ delete $Expect{$File::Find::dir}
+ }
+ return @files;
+}
+
+sub my_postprocess {
+ print "# POSTPROCESS: \$File::Find::dir => '$File::Find::dir' \n";
+ delete $Expect{$File::Find::dir};
+}
+
+
+if ($^O eq 'MacOS') {
+
+ MkDir( 'for_find',0770 );
+ CheckDie(chdir(for_find));
+
+ $cwd = cwd(); # save cwd
+ ( $cwd_untainted ) = $cwd =~ m|^(.+)$|; # untaint it
+
+ MkDir( 'fa',0770 );
+ MkDir( 'fb',0770 );
+ touch(':fb:fb_ord');
+ MkDir( ':fb:fba',0770 );
+ touch(':fb:fba:fba_ord');
+ CheckDie( symlink(':fb',':fa:fsl') ) if $symlink_exists;
+ touch(':fa:fa_ord');
+
+ MkDir( ':fa:faa',0770 );
+ touch(':fa:faa:faa_ord');
+ MkDir( ':fa:fab',0770 );
+ touch(':fa:fab:fab_ord');
+ MkDir( ':fa:fab:faba',0770 );
+ touch(':fa:fab:faba:faba_ord');
+
+ %Expect = (':' => 1, 'fsl' => 1, 'fa_ord' => 1, 'fab' => 1, 'fab_ord' => 1,
+ 'faba' => 1, 'faa' => 1, 'faa_ord' => 1);
+ delete $Expect{'fsl'} unless $symlink_exists;
+ %Expect_Dir = (':' => 1, 'fa' => 1, 'faa' => 1, 'fab' => 1, 'faba' => 1,
+ 'fb' => 1, 'fba' => 1);
+ delete @Expect_Dir{'fb','fba'} unless $symlink_exists;
+ File::Find::find( {wanted => \&wanted, untaint => 1},':fa' );
+ Check( scalar(keys %Expect) == 0 );
+
+ %Expect=(':fa' => 1, ':fa:fsl' => 1, ':fa:fa_ord' => 1, ':fa:fab' => 1,
+ ':fa:fab:fab_ord' => 1, ':fa:fab:faba' => 1,
+ ':fa:fab:faba:faba_ord' => 1, ':fa:faa' => 1, ':fa:faa:faa_ord' => 1);
+ delete $Expect{':fa:fsl'} unless $symlink_exists;
+ %Expect_Dir = (':fa' => 1, ':fa:faa' => 1, ':fa:fab' => 1, ':fa:fab:faba' => 1,
+ ':fb' => 1, ':fb:fba' => 1);
+ delete @Expect_Dir{':fb',':fb:fba'} unless $symlink_exists;
+ File::Find::find( {wanted => \&wanted, no_chdir => 1, untaint => 1},':fa' );
+ Check( scalar(keys %Expect) == 0 );
+
+ %Expect=(':' => 1, ':fa' => 1, ':fa:fsl' => 1, ':fa:fa_ord' => 1, ':fa:fab' => 1,
+ ':fa:fab:fab_ord' => 1, ':fa:fab:faba' => 1,
+ ':fa:fab:faba:faba_ord' => 1, ':fa:faa' => 1, ':fa:faa:faa_ord' => 1,
+ ':fb' => 1, ':fb:fba' => 1, ':fb:fba:fba_ord' => 1, ':fb:fb_ord' => 1);
+ delete $Expect{':fa:fsl'} unless $symlink_exists;
+ %Expect_Dir = (':' => 1, ':fa' => 1, ':fa:faa' => 1, ':fa:fab' => 1, ':fa:fab:faba' => 1,
+ ':fb' => 1, ':fb:fba' => 1);
+ delete @Expect_Dir{':fb',':fb:fba'} unless $symlink_exists;
+ File::Find::finddepth( {wanted => \&dn_wanted, untaint => 1 },':' );
+ Check( scalar(keys %Expect) == 0 );
+
+ %Expect=(':' => 1, ':fa' => 1, ':fa:fsl' => 1, ':fa:fa_ord' => 1, ':fa:fab' => 1,
+ ':fa:fab:fab_ord' => 1, ':fa:fab:faba' => 1,
+ ':fa:fab:faba:faba_ord' => 1, ':fa:faa' => 1, ':fa:faa:faa_ord' => 1,
+ ':fb' => 1, ':fb:fba' => 1, ':fb:fba:fba_ord' => 1, ':fb:fb_ord' => 1);
+ delete $Expect{':fa:fsl'} unless $symlink_exists;
+ %Expect_Dir = (':' => 1, ':fa' => 1, ':fa:faa' => 1, ':fa:fab' => 1, ':fa:fab:faba' => 1,
+ ':fb' => 1, ':fb:fba' => 1);
+ delete @Expect_Dir{':fb',':fb:fba'} unless $symlink_exists;
+ File::Find::finddepth( {wanted => \&d_wanted, no_chdir => 1, untaint => 1 },':' );
+ Check( scalar(keys %Expect) == 0 );
+
+ # untaint, preprocess and postprocess tests below added by Thomas Wegner, 17-05-2001
+
+ print "# check untainting (no follow)\n";
+ # don't untaint at all
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted},':fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|Insecure dependency| );
+ chdir($cwd_untainted);
+
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, untaint => 1,
+ untaint_pattern => qr|^(NO_MATCH)$|},':fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|is still tainted| );
+ chdir($cwd_untainted);
+
+ print "# check untaint_skip (no follow)\n";
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, untaint => 1, untaint_skip => 1,
+ untaint_pattern => qr|^(NO_MATCH)$|}, ':fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|insecure cwd| );
+ chdir($cwd_untainted);
+
+ print "# check preprocess\n";
+ %Expect=(
+ ':' => {fa => 1, fb => 1},
+ ':fa:' => {faa => 1, fab => 1, fa_ord => 1},
+ ':fa:faa:' => {faa_ord => 1},
+ ':fa:fab:' => {faba => 1, fab_ord => 1},
+ ':fa:fab:faba:' => {faba_ord => 1},
+ ':fb:' => {fba => 1, fb_ord => 1},
+ ':fb:fba:' => {fba_ord => 1}
+ );
+ File::Find::find( {wanted => \&noop_wanted, untaint => 1, preprocess => \&my_preprocess}, ':' );
+ Check( scalar(keys %Expect) == 0 );
+
+ print "# check postprocess\n";
+ %Expect=(':' => 1, ':fa:' => 1, ':fa:faa:' => 1, ':fa:fab:' => 1, ':fa:fab:faba:' => 1, ':fb:' => 1,
+ ':fb:fba:' => 1 );
+ File::Find::find( {wanted => \&noop_wanted, untaint => 1, postprocess => \&my_postprocess}, ':' );
+ Check( scalar(keys %Expect) == 0 );
+
+ # Verify that File::Find::find will call wanted even if the topdir of
+ # is a symlink to a directory, and it shouldn't follow the link
+ # unless follow is set, which it isn't in this case
+ %Expect = ('fsl' => 1);
+ %Expect_Dir = ();
+ File::Find::find( {wanted => \&wanted, untaint => 1},':fa:fsl' );
+ Check( scalar(keys %Expect) == 0 );
+
+ if ( $symlink_exists ) {
+ $FastFileTests_OK= 1;
+ %Expect=(':' => 1, 'fa_ord' => 1, 'fsl' => 1, 'fb_ord' => 1, 'fba' => 1,
+ 'fba_ord' => 1, 'fab' => 1, 'fab_ord' => 1, 'faba' => 1, 'faa' => 1,
+ 'faa_ord' => 1);
+ %Expect_Dir = (':' => 1, 'fa' => 1, 'faa' => 1, 'fab' => 1, 'faba' => 1,
+ 'fb' => 1, 'fba' => 1);
+ File::Find::find( {wanted => \&wanted, follow_fast => 1, untaint => 1},':fa' );
+ Check( scalar(keys %Expect) == 0 );
+
+ %Expect=(':fa' => 1, ':fa:fa_ord' => 1, ':fa:fsl' => 1, ':fa:fsl:fb_ord' => 1,
+ ':fa:fsl:fba' => 1, ':fa:fsl:fba:fba_ord' => 1, ':fa:fab' => 1,
+ ':fa:fab:fab_ord' => 1, ':fa:fab:faba' => 1, ':fa:fab:faba:faba_ord' => 1,
+ ':fa:faa' => 1, ':fa:faa:faa_ord' => 1);
+ %Expect_Dir = (':fa' => 1, ':fa:faa' => 1, ':fa:fab' => 1, ':fa:fab:faba' => 1,
+ ':fb' => 1, ':fb:fba' => 1);
+ File::Find::find( {wanted => \&wanted, follow_fast => 1, no_chdir => 1, untaint => 1 },':fa' );
+ Check( scalar(keys %Expect) == 0 );
+
+ %Expect=(':fa' => 1, ':fa:fa_ord' => 1, ':fa:fsl' => 1, ':fa:fsl:fb_ord' => 1,
+ ':fa:fsl:fba' => 1, ':fa:fsl:fba:fba_ord' => 1, ':fa:fab' => 1,
+ ':fa:fab:fab_ord' => 1, ':fa:fab:faba' => 1, ':fa:fab:faba:faba_ord' => 1,
+ ':fa:faa' => 1, ':fa:faa:faa_ord' => 1);
+ %Expect_Dir = (':fa' => 1, ':fa:faa' => 1, ':fa:fab' => 1, ':fa:fab:faba' => 1,
+ ':fb' => 1, ':fb:fba' => 1);
+ File::Find::finddepth( {wanted => \&dn_wanted, follow_fast => 1, untaint => 1 },':fa' );
+ Check( scalar(keys %Expect) == 0 );
+
+ %Expect=(':fa' => 1, ':fa:fa_ord' => 1, ':fa:fsl' => 1, ':fa:fsl:fb_ord' => 1,
+ ':fa:fsl:fba' => 1, ':fa:fsl:fba:fba_ord' => 1, ':fa:fab' => 1,
+ ':fa:fab:fab_ord' => 1, ':fa:fab:faba' => 1, ':fa:fab:faba:faba_ord' => 1,
+ ':fa:faa' => 1, ':fa:faa:faa_ord' => 1);
+ %Expect_Dir = (':fa' => 1, ':fa:faa' => 1, ':fa:fab' => 1, ':fa:fab:faba' => 1,
+ ':fb' => 1, ':fb:fba' => 1);
+ File::Find::finddepth( {wanted => \&d_wanted, follow_fast => 1, no_chdir => 1, untaint => 1 },':fa' );
+ Check( scalar(keys %Expect) == 0 );
+
+ # tests below added by Thomas Wegner, 17-05-2001
+
+ print "# check dangling symbolic links\n";
+ MkDir( 'dangling_dir',0770 );
+ CheckDie( symlink('dangling_dir','dangling_dir_sl') );
+ rmdir 'dangling_dir';
+ touch('dangling_file');
+ CheckDie( symlink('dangling_file',':fa:dangling_file_sl') );
+ unlink 'dangling_file';
+
+ %Expect=(':' => 1, 'fa_ord' => 1, 'fsl' => 1, 'fb_ord' => 1, 'fba' => 1,
+ 'fba_ord' => 1, 'fab' => 1, 'fab_ord' => 1, 'faba' => 1, 'faba_ord' => 1,
+ 'faa' => 1, 'faa_ord' => 1);
+ %Expect_Dir = (':' => 1, 'fa' => 1, 'faa' => 1, 'fab' => 1, 'faba' => 1,
+ 'fb' => 1, 'fba' => 1);
+ undef $warn_msg;
+ File::Find::find( {wanted => \&d_wanted, follow => 1, untaint => 1 }, 'dangling_dir_sl', ':fa' );
+ Check( $warn_msg =~ m|dangling_dir_sl is a dangling symbolic link| );
+ unlink ':fa:dangling_file_sl', 'dangling_dir_sl';
+
+ print "# check recursion\n";
+ CheckDie( symlink(':fa:faa',':fa:faa:faa_sl') );
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, follow => 1, no_chdir => 1, untaint => 1 },':fa' ); };
+ print "# Died: $@";
+ Check( $@ =~ m|:for_find:fa:faa:faa_sl is a recursive symbolic link| );
+ unlink ':fa:faa:faa_sl';
+
+ print "# check follow_skip (file)\n";
+ CheckDie( symlink(':fa:fa_ord',':fa:fa_ord_sl') ); # symlink to a file
+ undef $@;
+ eval {File::Find::finddepth( {wanted => \&simple_wanted, follow => 1,follow_skip => 0,
+ no_chdir => 1, untaint => 1 },':fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|:for_find:fa:fa_ord encountered a second time| );
+
+ %Expect=(':fa' => 1, ':fa:fa_ord' => 1, ':fa:fsl' => 1, ':fa:fsl:fb_ord' => 1,
+ ':fa:fsl:fba' => 1, ':fa:fsl:fba:fba_ord' => 1, ':fa:fab' => 1,
+ ':fa:fab:fab_ord' => 1, ':fa:fab:faba' => 1, ':fa:fab:faba:faba_ord' => 1,
+ ':fa:faa' => 1, ':fa:faa:faa_ord' => 1);
+ %Expect_Dir = (':fa' => 1, ':fa:faa' => 1, ':fa:fab' => 1, ':fa:fab:faba' => 1,
+ ':fb' => 1, ':fb:fba' => 1);
+ File::Find::finddepth( {wanted => \&wanted, follow => 1, follow_skip => 1, no_chdir => 1,
+ untaint => 1 },':fa' );
+ Check( scalar(keys %Expect) == 0 );
+ unlink ':fa:fa_ord_sl';
+
+ print "# check follow_skip (directory)\n";
+ CheckDie( symlink(':fa:faa',':fa:faa_sl') ); # symlink to a directory
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, follow => 1, follow_skip => 0,
+ no_chdir => 1, untaint => 1 },':fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|:for_find:fa:faa: encountered a second time| );
+
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, follow => 1, follow_skip => 1,
+ no_chdir => 1, untaint => 1 },':fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|:for_find:fa:faa: encountered a second time| );
+
+ %Expect=(':fa' => 1, ':fa:fa_ord' => 1, ':fa:fsl' => 1, ':fa:fsl:fb_ord' => 1,
+ ':fa:fsl:fba' => 1, ':fa:fsl:fba:fba_ord' => 1, ':fa:fab' => 1,
+ ':fa:fab:fab_ord' => 1, ':fa:fab:faba' => 1, ':fa:fab:faba:faba_ord' => 1,
+ ':fa:faa' => 1, ':fa:faa:faa_ord' => 1);
+ %Expect_Dir = (':fa' => 1, ':fa:faa' => 1, ':fa:fab' => 1, ':fa:fab:faba' => 1,
+ ':fb' => 1, ':fb:fba' => 1);
+ File::Find::find( {wanted => \&wanted, follow => 1, follow_skip => 2, no_chdir => 1,
+ untaint => 1},':fa' );
+ Check( scalar(keys %Expect) == 0 );
+ unlink ':fa:faa_sl';
+
+ print "# check untainting (follow)\n";
+ # don't untaint at all
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, follow => 1},':fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|Insecure dependency| );
+ chdir($cwd_untainted);
+
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, follow => 1, untaint => 1,
+ untaint_pattern => qr|^(NO_MATCH)$|},':fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|is still tainted| );
+ chdir($cwd_untainted);
+
+ print "# check untaint_skip (follow)\n";
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, untaint => 1, untaint_skip => 1,
+ untaint_pattern => qr|^(NO_MATCH)$|}, ':fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|insecure cwd| );
+ chdir($cwd_untainted);
+
+ }
+
+} else {
+
+ MkDir( 'for_find',0770 );
+ CheckDie(chdir(for_find));
+
+ $cwd = cwd(); # save cwd
+ ( $cwd_untainted ) = $cwd =~ m|^(.+)$|; # untaint it
+
+ MkDir( 'fa',0770 );
+ MkDir( 'fb',0770 );
+ touch('fb/fb_ord');
+ MkDir( 'fb/fba',0770 );
+ touch('fb/fba/fba_ord');
+ CheckDie( symlink('../fb','fa/fsl') ) if $symlink_exists;
+ touch('fa/fa_ord');
+
+ MkDir( 'fa/faa',0770 );
+ touch('fa/faa/faa_ord');
+ MkDir( 'fa/fab',0770 );
+ touch('fa/fab/fab_ord');
+ MkDir( 'fa/fab/faba',0770 );
+ touch('fa/fab/faba/faba_ord');
+
+ %Expect = ('.' => 1, 'fsl' => 1, 'fa_ord' => 1, 'fab' => 1, 'fab_ord' => 1,
+ 'faba' => 1, 'faa' => 1, 'faa_ord' => 1);
+ delete $Expect{'fsl'} unless $symlink_exists;
+ %Expect_Dir = ('fa' => 1, 'faa' => 1, 'fab' => 1, 'faba' => 1,
+ 'fb' => 1, 'fba' => 1);
+ delete @Expect_Dir{'fb','fba'} unless $symlink_exists;
+ File::Find::find( {wanted => \&wanted, untaint => 1, untaint_pattern => qr|^(.+)$|},'fa' );
+ Check( scalar(keys %Expect) == 0 );
+
+ %Expect=('fa' => 1, 'fa/fsl' => 1, 'fa/fa_ord' => 1, 'fa/fab' => 1,
+ 'fa/fab/fab_ord' => 1, 'fa/fab/faba' => 1,
+ 'fa/fab/faba/faba_ord' => 1, 'fa/faa' => 1, 'fa/faa/faa_ord' => 1);
+ delete $Expect{'fa/fsl'} unless $symlink_exists;
+ %Expect_Dir = ('fa' => 1, 'fa/faa' => 1, '/fa/fab' => 1, 'fa/fab/faba' => 1,
+ 'fb' => 1, 'fb/fba' => 1);
+ delete @Expect_Dir{'fb','fb/fba'} unless $symlink_exists;
+ File::Find::find( {wanted => \&wanted, no_chdir => 1, untaint => 1, untaint_pattern => qr|^(.+)$|},'fa' );
+ Check( scalar(keys %Expect) == 0 );
+
+ %Expect=('.' => 1, './fa' => 1, './fa/fsl' => 1, './fa/fa_ord' => 1, './fa/fab' => 1,
+ './fa/fab/fab_ord' => 1, './fa/fab/faba' => 1,
+ './fa/fab/faba/faba_ord' => 1, './fa/faa' => 1, './fa/faa/faa_ord' => 1,
+ './fb' => 1, './fb/fba' => 1, './fb/fba/fba_ord' => 1, './fb/fb_ord' => 1);
+ delete $Expect{'./fa/fsl'} unless $symlink_exists;
+ %Expect_Dir = ('./fa' => 1, './fa/faa' => 1, '/fa/fab' => 1, './fa/fab/faba' => 1,
+ './fb' => 1, './fb/fba' => 1);
+ delete @Expect_Dir{'./fb','./fb/fba'} unless $symlink_exists;
+ File::Find::finddepth( {wanted => \&dn_wanted , untaint => 1, untaint_pattern => qr|^(.+)$|},'.' );
+ Check( scalar(keys %Expect) == 0 );
+
+ %Expect=('.' => 1, './fa' => 1, './fa/fsl' => 1, './fa/fa_ord' => 1, './fa/fab' => 1,
+ './fa/fab/fab_ord' => 1, './fa/fab/faba' => 1,
+ './fa/fab/faba/faba_ord' => 1, './fa/faa' => 1, './fa/faa/faa_ord' => 1,
+ './fb' => 1, './fb/fba' => 1, './fb/fba/fba_ord' => 1, './fb/fb_ord' => 1);
+ delete $Expect{'./fa/fsl'} unless $symlink_exists;
+ %Expect_Dir = ('./fa' => 1, './fa/faa' => 1, '/fa/fab' => 1, './fa/fab/faba' => 1,
+ './fb' => 1, './fb/fba' => 1);
+ delete @Expect_Dir{'./fb','./fb/fba'} unless $symlink_exists;
+ File::Find::finddepth( {wanted => \&d_wanted, no_chdir => 1, untaint => 1, untaint_pattern => qr|^(.+)$| },'.' );
+ Check( scalar(keys %Expect) == 0 );
+
+ # untaint, preprocess and postprocess tests below added by Thomas Wegner, 17-05-2001
+
+ print "# check untainting (no follow)\n";
+ # don't untaint at all
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted},'fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|Insecure dependency| );
+ chdir($cwd_untainted);
+
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, untaint => 1,
+ untaint_pattern => qr|^(NO_MATCH)$|},'fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|is still tainted| );
+ chdir($cwd_untainted);
+
+ print "# check untaint_skip (no follow)\n";
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, untaint => 1, untaint_skip => 1,
+ untaint_pattern => qr|^(NO_MATCH)$|}, 'fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|insecure cwd| );
+ chdir($cwd_untainted);
+
+ print "# check preprocess\n";
+ %Expect=(
+ '.' => {fa => 1, fb => 1},
+ './fa' => {faa => 1, fab => 1, fa_ord => 1},
+ './fa/faa' => {faa_ord => 1},
+ './fa/fab' => {faba => 1, fab_ord => 1},
+ './fa/fab/faba' => {faba_ord => 1},
+ './fb' => {fba => 1, fb_ord => 1},
+ './fb/fba' => {fba_ord => 1}
+ );
+
+ File::Find::find( {wanted => \&noop_wanted, preprocess => \&my_preprocess, untaint => 1,
+ untaint_pattern => qr|^(.+)$|}, '.' );
+ Check( scalar(keys %Expect) == 0 );
+
+ print "# check postprocess\n";
+ %Expect=('.' => 1, './fa' => 1, './fa/faa' => 1, './fa/fab' => 1, './fa/fab/faba' => 1, './fb' => 1,
+ './fb/fba' => 1 );
+ File::Find::find( {wanted => \&noop_wanted, postprocess => \&my_postprocess, untaint => 1,
+ untaint_pattern => qr|^(.+)$|}, '.' );
+ Check( scalar(keys %Expect) == 0 );
+
+ # Verify that File::Find::find will call wanted even if the topdir of
+ # is a symlink to a directory, and it shouldn't follow the link
+ # unless follow is set, which it isn't in this case
+ %Expect = ('fsl' => 1);
+ %Expect_Dir = ();
+ File::Find::find( {wanted => \&wanted, untaint => 1},'fa/fsl' );
+ Check( scalar(keys %Expect) == 0 );
+
+ if ( $symlink_exists ) {
+ $FastFileTests_OK= 1;
+ %Expect=('.' => 1, 'fa_ord' => 1, 'fsl' => 1, 'fb_ord' => 1, 'fba' => 1,
+ 'fba_ord' => 1, 'fab' => 1, 'fab_ord' => 1, 'faba' => 1, 'faa' => 1,
+ 'faa_ord' => 1);
+ %Expect_Dir = ('fa' => 1, 'fa/faa' => 1, '/fa/fab' => 1, 'fa/fab/faba' => 1,
+ 'fb' => 1, 'fb/fba' => 1);
+ File::Find::find( {wanted => \&wanted, follow_fast => 1, untaint => 1, untaint_pattern => qr|^(.+)$|},'fa' );
+ Check( scalar(keys %Expect) == 0 );
+
+ %Expect=('fa' => 1, 'fa/fa_ord' => 1, 'fa/fsl' => 1, 'fa/fsl/fb_ord' => 1,
+ 'fa/fsl/fba' => 1, 'fa/fsl/fba/fba_ord' => 1, 'fa/fab' => 1,
+ 'fa/fab/fab_ord' => 1, 'fa/fab/faba' => 1, 'fa/fab/faba/faba_ord' => 1,
+ 'fa/faa' => 1, 'fa/faa/faa_ord' => 1);
+ %Expect_Dir = ('fa' => 1, 'fa/faa' => 1, '/fa/fab' => 1, 'fa/fab/faba' => 1,
+ 'fb' => 1, 'fb/fba' => 1);
+ File::Find::find( {wanted => \&wanted, follow_fast => 1, no_chdir => 1, untaint => 1,
+ untaint_pattern => qr|^(.+)$|},'fa' );
+ Check( scalar(keys %Expect) == 0 );
+
+ %Expect=('fa' => 1, 'fa/fa_ord' => 1, 'fa/fsl' => 1, 'fa/fsl/fb_ord' => 1,
+ 'fa/fsl/fba' => 1, 'fa/fsl/fba/fba_ord' => 1, 'fa/fab' => 1,
+ 'fa/fab/fab_ord' => 1, 'fa/fab/faba' => 1, 'fa/fab/faba/faba_ord' => 1,
+ 'fa/faa' => 1, 'fa/faa/faa_ord' => 1);
+ %Expect_Dir = ('fa' => 1, 'fa/faa' => 1, '/fa/fab' => 1, 'fa/fab/faba' => 1,
+ 'fb' => 1, 'fb/fba' => 1);
+ File::Find::finddepth( {wanted => \&dn_wanted, follow_fast => 1, untaint => 1,
+ untaint_pattern => qr|^(.+)$|},'fa' );
+ Check( scalar(keys %Expect) == 0 );
+
+ %Expect=('fa' => 1, 'fa/fa_ord' => 1, 'fa/fsl' => 1, 'fa/fsl/fb_ord' => 1,
+ 'fa/fsl/fba' => 1, 'fa/fsl/fba/fba_ord' => 1, 'fa/fab' => 1,
+ 'fa/fab/fab_ord' => 1, 'fa/fab/faba' => 1, 'fa/fab/faba/faba_ord' => 1,
+ 'fa/faa' => 1, 'fa/faa/faa_ord' => 1);
+ %Expect_Dir = ('fa' => 1, 'fa/faa' => 1, '/fa/fab' => 1, 'fa/fab/faba' => 1,
+ 'fb' => 1, 'fb/fba' => 1);
+ File::Find::finddepth( {wanted => \&d_wanted, follow_fast => 1, no_chdir => 1,
+ untaint => 1, untaint_pattern => qr|^(.+)$|},'fa' );
+ Check( scalar(keys %Expect) == 0 );
+
+ # tests below added by Thomas Wegner, 17-05-2001
+
+ print "# check dangling symbolic links\n";
+ MkDir( 'dangling_dir',0770 );
+ CheckDie( symlink('dangling_dir','dangling_dir_sl') );
+ rmdir 'dangling_dir';
+ touch('dangling_file');
+ CheckDie( symlink('../dangling_file','fa/dangling_file_sl') );
+ unlink 'dangling_file';
+
+ %Expect=('.' => 1, 'fa_ord' => 1, 'fsl' => 1, 'fb_ord' => 1, 'fba' => 1,
+ 'fba_ord' => 1, 'fab' => 1, 'fab_ord' => 1, 'faba' => 1, 'faba_ord' => 1,
+ 'faa' => 1, 'faa_ord' => 1);
+ %Expect_Dir = ('fa' => 1, 'fa/faa' => 1, 'fa/fab' => 1, 'fa/fab/faba' => 1,
+ 'fb' => 1, 'fb/fba' => 1);
+ undef $warn_msg;
+ File::Find::find( {wanted => \&d_wanted, follow => 1, untaint => 1,
+ untaint_pattern => qr|^(.+)$|}, 'dangling_dir_sl', 'fa' );
+ Check( $warn_msg =~ m|dangling_dir_sl is a dangling symbolic link| );
+ unlink 'fa/dangling_file_sl', 'dangling_dir_sl';
+
+ print "# check recursion\n";
+ CheckDie( symlink('../faa','fa/faa/faa_sl') );
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, follow => 1, no_chdir => 1,
+ untaint => 1, untaint_pattern => qr|^(.+)$|},'fa' ); };
+ print "# Died: $@";
+ Check( $@ =~ m|for_find/fa/faa/faa_sl is a recursive symbolic link| );
+ unlink 'fa/faa/faa_sl';
+
+ print "# check follow_skip (file)\n";
+ CheckDie( symlink('./fa_ord','fa/fa_ord_sl') ); # symlink to a file
+ undef $@;
+ eval {File::Find::finddepth( {wanted => \&simple_wanted, follow => 1, follow_skip => 0, no_chdir => 1,
+ untaint => 1, untaint_pattern => qr|^(.+)$|},'fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|for_find/fa/fa_ord encountered a second time| );
+
+ %Expect=('fa' => 1, 'fa/fa_ord' => 1, 'fa/fsl' => 1, 'fa/fsl/fb_ord' => 1,
+ 'fa/fsl/fba' => 1, 'fa/fsl/fba/fba_ord' => 1, 'fa/fab' => 1,
+ 'fa/fab/fab_ord' => 1, 'fa/fab/faba' => 1, 'fa/fab/faba/faba_ord' => 1,
+ 'fa/faa' => 1, 'fa/faa/faa_ord' => 1);
+ %Expect_Dir = ('fa' => 1, 'fa/faa' => 1, '/fa/fab' => 1, 'fa/fab/faba' => 1,
+ 'fb' => 1, 'fb/fba' => 1);
+ File::Find::finddepth( {wanted => \&wanted, follow => 1, follow_skip => 1, no_chdir => 1,
+ untaint => 1, untaint_pattern => qr|^(.+)$|},'fa' );
+ Check( scalar(keys %Expect) == 0 );
+ unlink 'fa/fa_ord_sl';
+
+ print "# check follow_skip (directory)\n";
+ CheckDie( symlink('./faa','fa/faa_sl') ); # symlink to a directory
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, follow => 1, follow_skip => 0, no_chdir => 1,
+ untaint => 1, untaint_pattern => qr|^(.+)$|},'fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|for_find/fa/faa encountered a second time| );
+
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, follow => 1, follow_skip => 1, no_chdir => 1,
+ untaint => 1, untaint_pattern => qr|^(.+)$|},'fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|for_find/fa/faa encountered a second time| );
+
+ %Expect=('fa' => 1, 'fa/fa_ord' => 1, 'fa/fsl' => 1, 'fa/fsl/fb_ord' => 1,
+ 'fa/fsl/fba' => 1, 'fa/fsl/fba/fba_ord' => 1, 'fa/fab' => 1,
+ 'fa/fab/fab_ord' => 1, 'fa/fab/faba' => 1, 'fa/fab/faba/faba_ord' => 1,
+ 'fa/faa' => 1, 'fa/faa/faa_ord' => 1);
+ %Expect_Dir = ('fa' => 1, 'fa/faa' => 1, '/fa/fab' => 1, 'fa/fab/faba' => 1,
+ 'fb' => 1, 'fb/fba' => 1);
+ File::Find::find( {wanted => \&wanted, follow => 1, follow_skip => 2, no_chdir => 1,
+ untaint => 1, untaint_pattern => qr|^(.+)$|},'fa' );
+ Check( scalar(keys %Expect) == 0 );
+ unlink 'fa/faa_sl';
+
+ print "# check untainting (follow)\n";
+ # don't untaint at all
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, follow => 1},'fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|Insecure dependency| );
+ chdir($cwd_untainted);
+
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, follow => 1, untaint => 1,
+ untaint_pattern => qr|^(NO_MATCH)$|},'fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|is still tainted| );
+ chdir($cwd_untainted);
+
+ print "# check untaint_skip (follow)\n";
+ undef $@;
+ eval {File::Find::find( {wanted => \&simple_wanted, untaint => 1, untaint_skip => 1,
+ untaint_pattern => qr|^(NO_MATCH)$|}, 'fa' );};
+ print "# Died: $@";
+ Check( $@ =~ m|insecure cwd| );
+ chdir($cwd_untainted);
+
+ }
}
print "# of cases: $case\n";