bug#80508: coreutils ln creates bad symbolic link
Collin Funk <[email protected]> Fri, 27 Feb 2026 23:04:03 -0800
| Newsgroups | gmane.comp.gnu.core-utils.bugs |
|---|---|
| Message-ID | <[email protected]> |
Hi Glenn, Glenn <[email protected]> writes: > coreutils "ln" created bad symbolic link when I believe it should have > failed , because the symbolic link being created already existed. > > Created a directory "AA" > > Created symbolic link "aa" to directory "./AA" > > Created directory "BB" > > Created symbolic link to "aa" to "./BB", without first deleting the > original symbolic link "aa". > > Symbolic link creation of "aa" should fail on line 43 of test (ln -s > ./BB aa), because symbolic link "aa" already exists. > No error was reported and it created a bad symbolic link, inside > directory "AA" as "BB" to "./BB" ( see line 53 of test), > > Symbolic link "aa" to directory "AA" remained. It is not a bug, but I agree the behavior is a bit awkward. Copying the relevant text from POSIX [1]: SYNOPSIS ln [-fs] [-L|-P] source_file target_file ln [-fs] [-L|-P] source_file... target_dir DESCRIPTION [...] In the second synopsis form, the ln utility shall create a new directory entry for each source_file operand, at a destination path in the existing directory named by target_dir. If the -s option is specified, a symbolic link shall be created with the contents specified by each source_file operand (which need not name an existing file); otherwise, a hard link shall be created to each file named by a source_file operand. [...] The second synopsis form shall be assumed when the final operand names an existing directory. > glenn@cottage:~/ZZ> ln --version > ln (GNU coreutils) 9.6 > Copyright (C) 2025 Free Software Foundation, Inc. > License GPLv3+: GNU GPL version 3 or later > <https://gnu.org/licenses/gpl.html>. > This is free software: you are free to change and redistribute it. > There is NO WARRANTY, to the extent permitted by law. > > Written by Mike Parker and David MacKenzie. > > -------------- test follows > > glenn@cottage:~/ZZ> ls -lR > .: > total 0 > glenn@cottage:~/ZZ> mkdir AA > glenn@cottage:~/ZZ> ls -lR > .: > total 0 > drwxr-xr-x 2 glenn glenn 6 Feb 27 16:13 AA > > ./AA: > total 0 > glenn@cottage:~/ZZ> ln -s ./AA aa > glenn@cottage:~/ZZ> ls -lR > .: > total 0 > lrwxrwxrwx 1 glenn glenn 4 Feb 27 16:13 aa -> ./AA > drwxr-xr-x 2 glenn glenn 6 Feb 27 16:13 AA > > ./AA: > total 0 > glenn@cottage:~/ZZ> mkdir BB > glenn@cottage:~/ZZ> ls -lR > .: > total 0 > lrwxrwxrwx 1 glenn glenn 4 Feb 27 16:13 aa -> ./AA > drwxr-xr-x 2 glenn glenn 6 Feb 27 16:13 AA > drwxr-xr-x 2 glenn glenn 6 Feb 27 16:14 BB > > ./AA: > total 0 > > ./BB: > total 0 > glenn@cottage:~/ZZ> *ln -s ./BB aa* > glenn@cottage:~/ZZ> ls -lR > .: > total 0 > lrwxrwxrwx 1 glenn glenn 4 Feb 27 16:13 aa -> ./AA > drwxr-xr-x 2 glenn glenn 16 Feb 27 16:14 AA > drwxr-xr-x 2 glenn glenn 6 Feb 27 16:14 BB > > ./AA: > total 0 > *lrwxrwxrwx 1 glenn glenn 4 Feb 27 16:14 BB -> ./BB* > > ./BB: > total 0 > glenn@cottage:~/ZZ> When you invoked 'ln -s ./BB aa', "aa" was an existing directory resolving to "AA". Therefore, the second POSIX synopsis form is used and the "BB" symbolic link is created in "AA". You can use the --no-target-directory option, though. I think that is the behavior that you want [2]: $ mkdir AA; $ ln -s ./AA aa; $ ln --no-target-directory -s ./BB aa ln: failed to create symbolic link 'aa': File exists Collin [1] https://pubs.opengroup.org/onlinepubs/9799919799/utilities/ln.html [2] https://www.gnu.org/software/coreutils/manual/html_node/Target-directory.html