Fix more inconsistent behaviour with symlinks in paths.
Both redo and minimal/do were doing slightly weird things with symlinked directories, especially when combined with "..". For example, if x is a link to ., then x/x/x/x/../y should resolve to "../y", which is quite non-obvious. Added some tests to make sure this stays fixed.
This commit is contained in:
parent
1f64cc4525
commit
686c381109
13 changed files with 121 additions and 8 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -3,6 +3,7 @@
|
|||
/.do_built.dir
|
||||
/minimal/.do_built
|
||||
/minimal/.do_built.dir
|
||||
/minimal/y
|
||||
*~
|
||||
*.tmp
|
||||
*.did
|
||||
|
|
|
|||
2
clean.do
2
clean.do
|
|
@ -6,7 +6,7 @@ if [ -e .do_built ]; then
|
|||
done <.do_built
|
||||
fi
|
||||
[ -z "$DO_BUILT" ] && rm -rf .do_built .do_built.dir
|
||||
rm -rf minimal/.do_built minimal/.do_built.dir docs.out
|
||||
rm -rf minimal/.do_built minimal/.do_built.dir minimal/y docs.out
|
||||
redo t/clean docs/clean redo/clean
|
||||
rm -f *~ .*~ */*~ */.*~ *.pyc install.wrapper
|
||||
find . -name '*.tmp' -exec rm -f {} \;
|
||||
|
|
|
|||
5
do
5
do
|
|
@ -66,6 +66,11 @@ case $target in
|
|||
build && bin/redo $args "$target"
|
||||
;;
|
||||
test)
|
||||
# Be intentionally confusing about paths, to try to
|
||||
# detect bugs.
|
||||
rm -f 't/symlink path'
|
||||
ln -s .. 't/symlink path' || die 'failed to make test dir.'
|
||||
cd 't/symlink path/t/symlink path'
|
||||
# First test minimal/do
|
||||
build
|
||||
# Add ./redo to PATH so we launch with redo/sh as the shell
|
||||
|
|
|
|||
36
minimal/do
36
minimal/do
|
|
@ -204,6 +204,38 @@ _normpath()
|
|||
)
|
||||
|
||||
|
||||
# Prints a "real" path, with all symlinks resolved where possible.
|
||||
_realpath()
|
||||
{
|
||||
local path="$1" relto="$2" isabs= rest=
|
||||
if _startswith "$path" "/"; then
|
||||
isabs=1
|
||||
else
|
||||
path="${relto%/}/$path"
|
||||
fi
|
||||
(
|
||||
for d in $(seq 100); do
|
||||
#echo "Trying: $PWD--$path" >&2
|
||||
if cd -P "$path" 2>/dev/null; then
|
||||
# success
|
||||
pwd=$(/bin/pwd)
|
||||
#echo " chdir ok: $pwd--$rest" >&2
|
||||
np=$(_normpath "${pwd%/}/$rest" "$relto")
|
||||
if [ -n "$isabs" ]; then
|
||||
echo "$np"
|
||||
else
|
||||
_relpath "$np" "$relto"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
_dirsplit "${path%/}"
|
||||
path=$_dirsplit_dir
|
||||
rest="$_dirsplit_base/$rest"
|
||||
done
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
# List the possible names for default*.do files in dir $1 matching the target
|
||||
# pattern in $2. We stop searching when we find the first one that exists.
|
||||
_find_dofiles_pwd()
|
||||
|
|
@ -241,7 +273,7 @@ _find_dofiles()
|
|||
[ -n "$dodir" ] && dodir=${dodir%/}/
|
||||
#echo "_find_dofiles: '$dodir' '$dofile'" >&2
|
||||
_find_dofiles_pwd "$dodir" "$dofile" && return 0
|
||||
newdir=$(_normpath "${dodir}.." "$PWD")
|
||||
newdir=$(_realpath "${dodir}.." "$PWD")
|
||||
[ "$newdir" = "$dodir" ] && break
|
||||
dodir=$newdir
|
||||
done
|
||||
|
|
@ -369,7 +401,7 @@ _redo()
|
|||
i=$(_abspath "$i" "$startdir")
|
||||
(
|
||||
cd "$DO_STARTDIR" || return 99
|
||||
i=$(_normpath "$(_relpath "$i" "$PWD")" "$PWD")
|
||||
i=$(_realpath "$(_relpath "$i" "$PWD")" "$PWD")
|
||||
_dirsplit "$i"
|
||||
dir=$_dirsplit_dir base=$_dirsplit_base
|
||||
_do "$dir" "$base"
|
||||
|
|
|
|||
|
|
@ -130,6 +130,17 @@ check ".." _normpath ../ "$x"
|
|||
check ".." _normpath .. "$x"
|
||||
|
||||
|
||||
SECTION _realpath
|
||||
rm -rf y
|
||||
mkdir y
|
||||
ln -s . y/x
|
||||
check "/usr/__does_not/b" _realpath "/usr/__does_not/a/../b" "$x"
|
||||
check "foo" _realpath "y/x/x/x/x/x/../foo" "$PWD"
|
||||
check "$(/bin/pwd)/foo" _realpath "$PWD/y/x/x/x/x/x/../foo" "$PWD"
|
||||
check "foo/blam" _realpath "y/x/x/x/x/x/../foo/spam/../blam" "$PWD"
|
||||
check "$(/bin/pwd)/foo/blam" _realpath "$PWD/y/x/x/../foo/spam/../blam" "$PWD"
|
||||
|
||||
|
||||
SECTION _find_dofile
|
||||
check "test.do" _find_dofiles test
|
||||
check "test.do" _find_dofile test
|
||||
|
|
|
|||
|
|
@ -151,14 +151,28 @@ def check_sane():
|
|||
return not _insane
|
||||
|
||||
|
||||
def _realdirpath(t):
|
||||
"""Like realpath(), but don't follow symlinks for the last element.
|
||||
|
||||
redo needs this because targets can be symlinks themselves, and we want
|
||||
to talk about the symlink, not what it points at. However, all the path
|
||||
elements along the way could result in pathname aliases for a *particular*
|
||||
target, so we want to resolve it to one unique name.
|
||||
"""
|
||||
dname, fname = os.path.split(t)
|
||||
if dname:
|
||||
dname = os.path.realpath(dname)
|
||||
return os.path.join(dname, fname)
|
||||
|
||||
|
||||
_cwd = None
|
||||
def relpath(t, base):
|
||||
"""Given a relative or absolute path t, express it relative to base."""
|
||||
global _cwd
|
||||
if not _cwd:
|
||||
_cwd = os.getcwd()
|
||||
t = os.path.normpath(os.path.join(_cwd, t))
|
||||
base = os.path.normpath(base)
|
||||
t = os.path.normpath(_realdirpath(os.path.join(_cwd, t)))
|
||||
base = os.path.normpath(_realdirpath(base))
|
||||
tparts = t.split('/')
|
||||
bparts = base.split('/')
|
||||
for tp, bp in zip(tparts, bparts):
|
||||
|
|
@ -172,7 +186,9 @@ def relpath(t, base):
|
|||
return join('/', tparts)
|
||||
|
||||
|
||||
# Return a path for t, if cwd were the dirname of env.v.TARGET.
|
||||
# Return a relative path for t that will work after we do
|
||||
# chdir(dirname(env.v.TARGET)).
|
||||
#
|
||||
# This is tricky! STARTDIR+PWD is the directory for the *dofile*, when
|
||||
# the dofile was started. However, inside the dofile, someone may have done
|
||||
# a chdir to anywhere else. env.v.TARGET is relative to the dofile path, so
|
||||
|
|
|
|||
2
t/.gitignore
vendored
2
t/.gitignore
vendored
|
|
@ -3,5 +3,7 @@
|
|||
/shellfail
|
||||
/shelltest.warned
|
||||
/shelltest.failed
|
||||
/shlink
|
||||
/stress.log
|
||||
/symlink path
|
||||
/flush-cache
|
||||
|
|
|
|||
5
t/105-sympath/.gitignore
vendored
Normal file
5
t/105-sympath/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/*.dyn
|
||||
/src
|
||||
/x
|
||||
/y
|
||||
|
||||
31
t/105-sympath/all.do
Normal file
31
t/105-sympath/all.do
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
redo-ifchange ../flush-cache
|
||||
rm -f src
|
||||
: >src
|
||||
|
||||
for iter in 10 20; do
|
||||
rm -rf y
|
||||
rm -f x *.dyn static
|
||||
mkdir y
|
||||
: >y/static
|
||||
ln -s . y/x
|
||||
../flush-cache
|
||||
|
||||
(
|
||||
cd y/x/x/x/x/x
|
||||
IFS=$(printf '\n')
|
||||
redo-ifchange static x/x/x/static $PWD/static \
|
||||
$(/bin/pwd)/static /etc/passwd
|
||||
redo-ifchange $PWD/../static 2>/dev/null && exit 35
|
||||
redo-ifchange 1.dyn x/x/x/2.dyn $PWD/3.dyn \
|
||||
$PWD/../4.dyn $(/bin/pwd)/5.dyn
|
||||
)
|
||||
[ -e y/1.dyn ] || exit $((iter + 1))
|
||||
[ -e y/2.dyn ] || exit $((iter + 2))
|
||||
[ -e y/3.dyn ] || exit $((iter + 3))
|
||||
[ -e 4.dyn ] || exit $((iter + 4))
|
||||
[ -e y/5.dyn ] || exit $((iter + 5))
|
||||
|
||||
# Second iteration won't work in minimal/do since it only ever
|
||||
# builds things once.
|
||||
. ../skip-if-minimal-do.sh
|
||||
done
|
||||
2
t/105-sympath/clean.do
Normal file
2
t/105-sympath/clean.do
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
rm -rf y
|
||||
rm -f src x *.dyn *~ .*~
|
||||
2
t/105-sympath/default.dyn.do
Normal file
2
t/105-sympath/default.dyn.do
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
redo-ifchange src
|
||||
echo dynamic >$3
|
||||
|
|
@ -2,6 +2,6 @@
|
|||
sed 's/\.do$//' |
|
||||
xargs redo
|
||||
|
||||
rm -f broken shellfile shellfail shelltest.warned shelltest.failed \
|
||||
*~ .*~ stress.log flush-cache
|
||||
rm -f broken shellfile shellfail shelltest.warned shelltest.failed shlink \
|
||||
*~ .*~ stress.log flush-cache 'symlink path'
|
||||
rm -rf 'space home dir'
|
||||
|
|
|
|||
|
|
@ -474,6 +474,12 @@ set x y z
|
|||
x=$(printf "a%-5sc" "b")
|
||||
[ "$x" = "ab c" ] || warn 119
|
||||
|
||||
# Make sure cd supports -L and -P options properly
|
||||
rm -f shlink
|
||||
ln -s . shlink
|
||||
(cd -L shlink/shlink/shlink/../shlink) || fail 120
|
||||
(cd -P shlink/shlink/shlink/../shlink) && fail 121
|
||||
|
||||
[ -e shelltest.failed ] && exit 41
|
||||
[ -e shelltest.warned ] && exit 42
|
||||
exit 40
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue