Hi,
Bored Videos <[email protected]> writes:
> Hello,
>
> I found some strange behavior where ls thinks . is a directory, even
> if it's a symlink.
>
> Example:
>
> 0 17:45 ~ % mkdir original
> 0 17:45 ~ % ls -dl original
> drwxr-xr-x 2 s s 6 2026-10-31 17:45 original
> 0 17:45 ~ % ln -s original symlink-to-original
> 0 17:45 ~ % cd symlink-to-original
> 0 17:45 ~/symlink-to-original % rm -rf ../original
> 0 17:45 ~/symlink-to-original % ls -dl
> drwxr-xr-x 0 s s 6 2026-10-31 17:45 .
> 0 17:46 ~/symlink-to-original % ls -dl .
> drwxr-xr-x 0 s s 6 2026-10-31 17:45 .
> 0 17:46 ~/symlink-to-original %
>
> This also happens if you don't break the symlink by deleting original.
>
> System: Void Linux x86_64-musl, coreutils version 9.4. ~ is an XFS 5
> filesystem.
>
> If somebody could look into the issue and let me know what they find,
> I would really appreciate it.
It isn't very intuitive, but it is the expected behavior. The file names
"." and ".." are special as described by POSIX [1]:
The special filename dot shall refer to the directory specified by
its predecessor. The special filename dot-dot shall refer to the
parent directory of its predecessor directory. As a special case, in
the root directory, dot-dot may refer to the root directory itself.
In your example, the predecessor is "symlink-to-original", and the
directory specified by it is "original", the non-symlink.
It might be helpful to see the behavior of the readlink system call. We
can see that it returns "original" when given "symlink-to-original":
$ strace --quiet=all -e trace=readlink readlink symlink-to-original
>/dev/null
readlink("symlink-to-original", "original", 64) = 8
However, when given "." it returns EINVAL [2], meaning that the given
path does not refer to a symbolic link:
$ (cd symlink-to-original && strace --quiet=all -e trace=readlink readlink .
>/dev/null)
readlink(".", 0x18c274b0, 64) = -1 EINVAL (Invalid argument)
Note that this also applies when a "." path component is added, not just
when it refers to the current working directory:
$ strace --quiet=all -e trace=readlink readlink symlink-to-original/.
>/dev/null
readlink("symlink-to-original/.", 0x292174b0, 64) = -1 EINVAL (Invalid
argument)
Hope that helps. The "Pathname Resolution" section of POSIX has some
other behaviors described that you may be interested in. It isn't the
easiest thing in the world to read, though.
Collin
[1] https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap04.html
[2] https://pubs.opengroup.org/onlinepubs/9799919799/functions/readlink.html