Bug in ln(1), its manpage, or just misunderstanding?

Alexey Dokuchaev danfe at nsu.ru
Tue Jan 27 08:57:55 UTC 2015


Hi there,

Per man ln(1), it has -F option which:

    -F    If the target file already exists and is a directory, then remove
          it so that the link may occur.  The -F option should be used with
          either -f or -i options.  If none is specified, -f is implied.  The
          -F option is a no-op unless -s option is specified.

As I read it correctly, it basically removes empty directory so I can place
a link of the same name there:

    $ mkdir foo
    $ ln -sF /etc foo		# result should be: foo -> /etc

However, in ln.c, static int linkit(const char *source, const char *target,
int isdir) contains:

    /*
     * If the target is a directory (and not a symlink if hflag),
     * append the source's name.
     */
    if (isdir ||
        (lstat(target, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
        (!hflag && stat(target, &sb) == 0 && S_ISDIR(sb.st_mode))) {
            if (strlcpy(bbuf, source, sizeof(bbuf)) >= sizeof(bbuf) ||
                (p = basename(bbuf)) == NULL ||
                snprintf(path, sizeof(path), "%s/%s", target, p) >=
                ...

It seems that this logic should be dependent on -F flag, as otherwise source
name is always appended to target and condition that "target file already
exists and is a directory" is impossible to specify and assess:

    $ mkdir foo
    $ ln -sF /etc foo	# result is "foo/etc -> /etc", not "foo -> /etc"

The code is even more bogus: it silently removes directory (and yet returns
an error), which makes it idempotent:

    $ mkdir foo
    $ ln -sF / foo ; echo $?
    ln: foo//: No such file or directory
    1
    < no foo at this point >
    $ ln -sF / foo ; echo $?
    0
    < now foo -> / >
    $ ln -sF / foo ; echo $?
    ln: foo//: Is a directory
    1

./danfe


More information about the freebsd-hackers mailing list