zsh-workers
 help / color / mirror / code / Atom feed
* can we have an option for cd to do a plain chdir()
@ 2022-02-06 17:44 Stephane Chazelas
  2022-02-09 17:16 ` Bart Schaefer
  0 siblings, 1 reply; 3+ messages in thread
From: Stephane Chazelas @ 2022-02-06 17:44 UTC (permalink / raw)
  To: Zsh hackers list

I was surprised to see that after:

ln -s /proc/self/cwd/.. link; cd link

I ended up two directories up instead of one.

strace revealed:

$ strace -fe getcwd,readlink,chdir,fchdir zsh -c 'cd link'
readlink("/proc/self/fd/0", "/dev/pts/4", 4095) = 10
chdir("/home/chazelas/link")            = 0
chdir("/home/chazelas/link")            = 0
+++ exited with 0 +++

Not sure why the two chdir()s there.

That it prepends $PWD to the path is also a problem if the
current working directory has been renamed, but most shells do
that without -P.

It's better in that case with cd -P or with chaselinks:

$ strace -fe getcwd,readlink,chdir,fchdir zsh -c 'cd -P link'
readlink("/proc/self/fd/0", "/dev/pts/4", 4095) = 10
chdir("/home/chazelas/link")            = 0
readlink("/home", 0x7ffe6ba34f00, 4096) = -1 EINVAL (Invalid argument)
readlink("/home/chazelas", 0x7ffe6ba34f00, 4096) = -1 EINVAL (Invalid argument)
readlink("/home/chazelas/link", "/proc/self/cwd/..", 4096) = 17
readlink("/proc", 0x7ffe6ba2fe70, 4096) = -1 EINVAL (Invalid argument)
readlink("/proc/self", "1000438", 4096) = 7
readlink("/proc/1000438", 0x7ffe6ba2ade0, 4096) = -1 EINVAL (Invalid argument)
readlink("/proc/1000438/cwd", "/home", 4096) = 5
readlink("/home", 0x7ffe6ba2ade0, 4096) = -1 EINVAL (Invalid argument)
chdir("..")                             = 0
chdir("/home")                          = 0
+++ exited with 0 +++
~$ strace -fe getcwd,readlink,chdir,fchdir zsh -o chaselinks -c 'cd link'
readlink("/proc/self/fd/0", "/dev/pts/4", 4095) = 10
readlink("/home", 0x7ffdfc3e0f40, 4096) = -1 EINVAL (Invalid argument)
readlink("/home/chazelas", 0x7ffdfc3e0f40, 4096) = -1 EINVAL (Invalid argument)
chdir("/home/chazelas/link")            = 0
readlink("/home", 0x7ffdfc3df960, 4096) = -1 EINVAL (Invalid argument)
readlink("/home/chazelas", 0x7ffdfc3df960, 4096) = -1 EINVAL (Invalid argument)
readlink("/home/chazelas/link", "/proc/self/cwd/..", 4096) = 17
readlink("/proc", 0x7ffdfc3da8d0, 4096) = -1 EINVAL (Invalid argument)
readlink("/proc/self", "1002598", 4096) = 7
readlink("/proc/1002598", 0x7ffdfc3d5840, 4096) = -1 EINVAL (Invalid argument)
readlink("/proc/1002598/cwd", "/home", 4096) = 5
readlink("/home", 0x7ffdfc3d5840, 4096) = -1 EINVAL (Invalid argument)
chdir("..")                             = 0
chdir("/home")                          = 0
+++ exited with 0 +++

But that sounds a bit overkill and racy.

In scripts, you usually want your cd's to do plain chdir()s but
there are many things that get in the way:

- the need for -P to avoid the logical traversal
- the need to -- as usual for avoid problems with dirnames
  starting with -
- CDPATH (it is inherited from the environment but at least when
  not in POSIX mode, that doesn't take precedence over actual
  relative path).
- The "-" string that is taken as $OLDPWD
- Also in zsh, the -n +n ones.
- and then there's that special fancy chasing of symlinks

So, to do an actual chdir, we need something like:

chdir() {
  case $1 in
    (/*) cd -P "$1";;
    ('') print -u2 "not sure out to chdir to an empty string";;
    (*) cd -P "./$1";;
  esac
}

Which addresses the first 5 points, but not the 6th.

It would be nice if we could just do for instance cd -r -- $dir
which would just a do chdir($dir) and set $PWD to getcwd() if
successful.

(or have chdir (which atm exits as a builtin for tcsh junkies I
suppose) do that and not take any option).

What do you think?

-- 
Stephane


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: can we have an option for cd to do a plain chdir()
  2022-02-06 17:44 can we have an option for cd to do a plain chdir() Stephane Chazelas
@ 2022-02-09 17:16 ` Bart Schaefer
  2022-02-10 20:27   ` Stephane Chazelas
  0 siblings, 1 reply; 3+ messages in thread
From: Bart Schaefer @ 2022-02-09 17:16 UTC (permalink / raw)
  To: Zsh hackers list

I started looking at this myself a while back but couldn't find a way
to untangle the function dependencies.  They're oddly factored and
other parts of the shell depend on some of the bits in the middle.
For example ...

On Sun, Feb 6, 2022 at 9:44 AM Stephane Chazelas <stephane@chazelas.org> wrote:
>
> $ strace -fe getcwd,readlink,chdir,fchdir zsh -c 'cd link'
> readlink("/proc/self/fd/0", "/dev/pts/4", 4095) = 10
> chdir("/home/chazelas/link")            = 0
> chdir("/home/chazelas/link")            = 0
> +++ exited with 0 +++
>
> Not sure why the two chdir()s there.

I'm pretty sure that's because there's first a chdir() via
cd_try_chdir() which might fail (e.g., searching cdpath), and then
once the full path has been confirmed there's another chdir() to make
sure the shell is actually in the directory that was found.

It has long felt to me as though the whole thing could be simplified,
but I set it aside after a few of my attempts broke various
special-case semantics.  I'm also not certain that Test/B01cd.ztst is
comprehensive in terms of catching possible regressions.


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: can we have an option for cd to do a plain chdir()
  2022-02-09 17:16 ` Bart Schaefer
@ 2022-02-10 20:27   ` Stephane Chazelas
  0 siblings, 0 replies; 3+ messages in thread
From: Stephane Chazelas @ 2022-02-10 20:27 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

2022-02-09 09:16:46 -0800, Bart Schaefer:
> I started looking at this myself a while back but couldn't find a way
> to untangle the function dependencies.  They're oddly factored and
> other parts of the shell depend on some of the bits in the middle.
[...]

Thanks, that's the impression I had from glancing at the code.

But then what about the idea of having an option like -r that
bypasses all that and does the simple chdir().

BTW, I've just thought of another way to do the equivalent of a
chdir() reliably on Linux provided you have read access to a
directory:

chdir() { cd /dev/fd/3 3< "$1"; }

Beside adding a "cd -r whatever" that just does a
chdir(whatever), we could add:

- "cd -u 3" which does a fchdir(3)
- sysopen -o path -u fd some/path && cd -u "$fd"
  (or sysopen -pu fd some/path as it's neither a read nor write
  open)
  (with -o nofollow, that can help do some safe directory
  traversal)
- sysopen -@ "$fd" -u otherfd some/path
  for openat()

Being able to do fchdir would allow improvements over the
less reliable:

for dir in $dirs; do
  cd $dir
  do-something
  cd ..
done

Or

for dir in $dirs; do
  pushd $dir
  do-something
  popd
done

Which we could replace with:

savedir() sysopen -po cloexec -u "$1" .
chdir_no_follow() {
  local fd ret
  sysopen -po nofollow -u fd -- "$1" || return
  cd -u "$fd"; ret=$?
  exec {fd}<&-
  return "$ret"
}
savedir || exit
for dir in $dirs; do
  chdir_no_follow $dir && do-something
  cd -u "$fd"
done

What do you think?

-- 
Stephane


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2022-02-10 20:27 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-06 17:44 can we have an option for cd to do a plain chdir() Stephane Chazelas
2022-02-09 17:16 ` Bart Schaefer
2022-02-10 20:27   ` Stephane Chazelas

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).