#!perl -l use strict; use warnings; use File::Spec; my %seen; # Find the last symlink in the path # Find the absolute path to that symlink # Tack on whatever is after it # squeeze out .. entries sub true_path { my ($path, $base, $stuff_to_tack_on) = @_; $path = File::Spec->rel2abs( $path, $base ) ; #print "*** Checking <$path>"; die "Circular!" if $seen{$path}++; my $last_symlink = ''; my $stuff_after_last_symlink = ''; my $built_path = ''; for my $dir (File::Spec->splitdir($path)) { my $test_path = File::Spec->catdir($built_path, $dir); if (-l $test_path) { #print "Found symlink $test_path"; $base = $built_path; $last_symlink = readlink $test_path; $stuff_after_last_symlink = ''; } else { #print "non-symlink $test_path"; $stuff_after_last_symlink = File::Spec->catdir($stuff_after_last_symlink, $dir); } $built_path = $test_path; } if ($last_symlink ne '') { #print "Symlink=$last_symlink; Base=$base"; true_path($last_symlink, $base, File::Spec->catdir($stuff_after_last_symlink, $stuff_to_tack_on)); } else { # Safe from symlinks, strip out foo/.. my @dirlist = (); for my $dir (File::Spec->splitdir(File::Spec->catdir($built_path, $stuff_to_tack_on))) { if ($dir eq '..') { pop @dirlist } else { push @dirlist, $dir }; } $path = @dirlist ? File::Spec->catdir(@dirlist) : ''; } } s/\s+$//, %seen=(), print "$_\n -->", true_path($_, '', '') while ; __DATA__ /var/log/../../home/poletti/../../etc/passwd . /var/../..