[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Testing sub directories using perl AGAIN

Shao Zhang wrote:
>     But it is still not exactly what I want. All the answers used the
> idea to compare the string, but what about
> the following case:
> dir1 = "/www/info/world";
> dir2 = "/www/info/world/usa/../../../."
> Now, clearly dir1 is a sub directory of dir2. So how do I test. Since
> the security is a big concern of us, we cannot
> afford the case like above.

Here's a quick solution since I had some code that can do it lying around
from something else. Probably overkill or not ideal, but it should work.

if (GetAbsolutePath(DeSymlinkPath(GetAbsolutePath($dir1))) eq
    GetAbsolutePath(DeSymlinkPath(GetAbsolutePath($dir2)))) {
	# whatever

And have these functions defined:

# Passed a filename, find all elements of it that are symlinks, and replace 
# with the symlink destination directory names. Calls itself recursivly until
# no more symlinks are left in the filename.
# Note that the path this returns may be ugly and have lots of extra /'s and
# ..'s and .'s in it. Use GetAbsolutePath to clean it up. Also note that this 
# only works if it's passed an absulte path to begin with. Therefore, a 
# typical invocation will be something like:
# GetAbsolutePath(DeSymlinkPath(GetAbsolutePath(file)))
sub DeSymlinkPath { $_=shift;
	my $dirty=undef; # set to 1 if we encounter a symlink.
	my @list=split(m:/:, $_);

	my $a=undef;
	foreach $elt (@list) {
		if (-l "$a/$elt") {
			my $b=readlink("$a/$elt");
			if ($b=~m:^/: eq undef) { # relative symlink, add to current pwd.
			else { # absolute symlink, replaces current pwd.
		else { # normal directory or file, add to pwd.

	if ($dirty) {
		return DeSymlinkPath($a);
	else {
		return $a;

# Passed a filname that may be relative, determine the absolute filename. 
# So we have to get rid of relative pathnames, and we even have to handle 
# things like ./../../../usr/X11R5/../X11R6/bin/./foo
sub GetAbsolutePath { $_=shift;
	if (m:^/: eq undef) { # doesn't start with / , so is a relative path.
		my $pwd=`pwd`; # isn't there a perl function for this?
		chomp $pwd;

	tr:/:/:s; # replace all // with /

	my @dirlist;
	foreach $dir (split(m:/:, $_)) {
		if ($dir eq '..') {
			pop @dirlist; # go down 1 directory.
		elsif ($dir ne '.') {
			push (@dirlist,$dir);

	$_='/'. join('/',@dirlist);
	tr:/:/:s; # replace all // with /

	return $_;

see shy jo

Reply to: