Bug 254489 - ln(1) should apply -F to the file being created, not target_file (was command 'ln -sfF' behaves unreasonably: it deletes the target directory and then fails)
Summary: ln(1) should apply -F to the file being created, not target_file (was command...
Status: New
Alias: None
Product: Base System
Classification: Unclassified
Component: bin (show other bugs)
Version: 12.2-STABLE
Hardware: Any Any
: --- Affects Only Me
Assignee: freebsd-bugs (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-03-22 20:44 UTC by Yuri Victorovich
Modified: 2021-03-23 00:02 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Yuri Victorovich freebsd_committer freebsd_triage 2021-03-22 20:44:52 UTC
Command:
> mkdir -p .x
> ln -sfF /bin/ls .x/

fails:
> ln: .x/: No such file or directory

The expected result is to create a symlink .x/ls -> /bin/ls
Comment 1 Christos Margiolis freebsd_committer freebsd_triage 2021-03-22 21:49:57 UTC
There is no bug on ln(1)'s part. The -F option checks if your last argument is a directory and if it is, it removes it only if it's empty (see code below).


static int	Fflag;			/* Remove empty directories also. */


if (Fflag && S_ISDIR(sb.st_mode)) {
	if (rmdir(target)) {
		warn("%s", target);
		return (1);
        }
}

The reason your command fails doesn't have to do with the -F option - it has to do with the fact that you're trying to create a link named `.x/`, and as you probably know, you're not allowed to use slashes inside a name, so symlink(2) fails. You'd get the same error no matter what option you used.

If you want your command to work, simply write it as `ln -sfF /bin/ls .x`.
Comment 2 Yuri Victorovich freebsd_committer freebsd_triage 2021-03-22 22:29:58 UTC
> The -F option checks if your last argument is a directory and if it is, it removes it only if it's empty (see code below).


But this isn't what ln(1) says:
> -F     If the target file already exists and is a directory, then remove
>        it so that the link may occur.

The target of the link in this case is a file .x/ls and it doesn't exist.
Comment 3 Yuri Victorovich freebsd_committer freebsd_triage 2021-03-22 22:38:36 UTC
There is an ambiguity here: target_file vs. target of the link.

When the user specifies "-F" he means "target of the link" (the object being created). IMO a correct fix here is to fix ln(1) to apply "-F" to the "target of the link", and to correct the manpage accordingly.
Comment 4 Christos Margiolis freebsd_committer freebsd_triage 2021-03-22 23:34:57 UTC
> The target of the link in this case is a file .x/ls and it doesn't exist.

It seems that the -F option interprets `.x/` literally, so it tries to create a file named `.x/` which obviously doesn't work. You'd have to write the command as `ln -sfF /bin/ls .x/ls` in order for it work the way you want it to.

> When the user specifies "-F" he means "target of the link" (the object
> being created). IMO a correct fix here is to fix ln(1) to apply "-F" to
> the "target of the link", and to correct the manpage accordingly.

I think the reason behind the fact that -F doesn't "fill in" the target's name 
(i.e `.x/` to `.x/ls`) is to prevent commands such as `ln -sfF foo .` which would mean that the current working directory has to be deleted if it's empty.

It makes sense that -F doesn't have the same behavior as the other options since it involves deleting stuff and the command I mentioned above is a common command and very easy way to mess things up, especially if -F is used inside a script.
Comment 5 Yuri Victorovich freebsd_committer freebsd_triage 2021-03-23 00:02:07 UTC
> [...] to prevent commands such as `ln -sfF foo .` which would mean that the current working directory has to be deleted if it's empty

ln(1) can make exceptions for corner cases like this.