1#!/bin/sh 2# -*- perl -*- 3# Ensure that pwd works even when run from a very deep directory. 4 5# Copyright (C) 2006-2023 Free Software Foundation, Inc. 6 7# This program is free software: you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation, either version 3 of the License, or 10# (at your option) any later version. 11 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16 17# You should have received a copy of the GNU General Public License 18# along with this program. If not, see <https://www.gnu.org/licenses/>. 19 20. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src 21print_ver_ pwd 22 23require_readable_root_ 24require_perl_ 25 26ARGV_0=$0 27export ARGV_0 28 29# Don't use CuTmpdir here, since File::Temp's use of rmtree can't 30# remove the deep tree we create. 31$PERL -Tw -I"$abs_srcdir/tests" -MCuSkip -- - <<\EOF 32 33# Show that pwd works even when the length of the resulting 34# directory name is longer than PATH_MAX. 35use strict; 36 37(my $ME = $ENV{ARGV_0}) =~ s|.*/||; 38 39sub normalize_to_cwd_relative ($$$) 40{ 41 my ($dir, $dev, $ino) = @_; 42 my $slash = -1; 43 my $next_slash; 44 while (1) 45 { 46 $slash = index $dir, '/', $slash + 1; 47 $slash <= -1 48 and die "$ME: $dir does not contain old CWD\n"; 49 my $dir_prefix = $slash ? substr ($dir, 0, $slash) : '/'; 50 my ($d, $i) = (stat $dir_prefix)[0, 1]; 51 defined $d && defined $i 52 or die "$ME: $dir_prefix: stat failed: $!\n"; 53 $d eq $dev && $i eq $ino 54 and return substr $dir, $slash + 1; 55 } 56} 57 58# Set up a safe, well-known environment 59$ENV{IFS} = ''; 60 61# Taint checking requires a sanitized $PATH. This script performs no $PATH 62# search, so on most Unix-based systems, it is fine simply to clear $ENV{PATH}. 63# However, on Cygwin, it's used to find cygwin1.dll, so set it. 64$ENV{PATH} = '/bin:/usr/bin'; 65 66# Save CWD's device and inode numbers. 67my ($dev, $ino) = (stat '.')[0, 1]; 68 69# Construct the expected "."-relative part of pwd's output. 70my $z = 'z' x 31; 71my $n = 256; 72my $expected = "/$z" x $n; 73# Remove the leading "/". 74substr ($expected, 0, 1) = ''; 75 76my $i = 0; 77do 78 { 79 mkdir $z, 0700 80 or CuSkip::skip "$ME: skipping this test; cannot create long " 81 . "directory name at depth $i: $!\n"; 82 chdir $z 83 } 84until (++$i == $n); 85 86my $abs_top_builddir = $ENV{abs_top_builddir}; 87$abs_top_builddir 88 or die "$ME: envvar abs_top_builddir not defined\n"; 89my $build_src_dir = "$abs_top_builddir/src"; 90$build_src_dir =~ m!^([-+.:/\w]+)$! 91 or CuSkip::skip "$ME: skipping this test; odd build source directory name:\n" 92 . "$build_src_dir\n"; 93$build_src_dir = $1; 94 95my $pwd_binary = "$build_src_dir/pwd"; 96 97-x $pwd_binary 98 or die "$ME: $pwd_binary is not an executable file\n"; 99chomp (my $actual = qx!$pwd_binary!); 100 101# Convert the absolute name from pwd into a $CWD-relative name. 102# This is necessary in order to avoid a spurious failure when run 103# from a directory in a bind-mounted partition. What happens is 104# pwd reads a ".." that contains two or more entries with identical 105# dev,ino that match the ones we're looking for, and it chooses a 106# name that does not correspond to the one already recorded in $CWD. 107$actual = normalize_to_cwd_relative $actual, $dev, $ino; 108 109if ($expected ne $actual) 110 { 111 my $e_len = length $expected; 112 my $a_len = length $actual; 113 warn "expected len: $e_len\n"; 114 warn "actual len: $a_len\n"; 115 warn "expected: $expected\n"; 116 warn "actual: $actual\n"; 117 exit 1; 118 } 119EOF 120 121fail=$? 122 123Exit $fail 124