Bug 73301 - Documentation misses case of link, linkat, symlink, symlinkat giving ENOENT for a directory with a reference only held by a process
Summary: Documentation misses case of link, linkat, symlink, symlinkat giving ENOENT f...
Status: RESOLVED CODE_FIX
Alias: None
Product: Documentation
Classification: Unclassified
Component: man-pages (show other bugs)
Hardware: All Linux
: P1 low
Assignee: documentation_man-pages@kernel-bugs.osdl.org
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-03-31 19:09 UTC by Steven Stewart-Gallus
Modified: 2014-05-05 11:06 UTC (History)
1 user (show)

See Also:
Kernel Version:
Subsystem:
Regression: No
Bisected commit-id:


Attachments

Description Steven Stewart-Gallus 2014-03-31 19:09:23 UTC
The errors printed out by the following code on symlink and symlinkat are missed by the documentation.

#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
    if (-1 == mkdir("/tmp/temporary", S_IRWXU)) {
        perror("mkdir");
    }

    int dir = open("/tmp/temporary", O_RDONLY | O_CLOEXEC);
    if (-1 == dir) {
        perror("open");
    }

    if (-1 == rmdir("/tmp/temporary")) {
        perror("rmdir");
    }

    if (-1 == symlinkat("/", dir, "root")) {
        perror("symlinkat");
    }

    char template_text[] = "/proc/self/fd/XXXXXXXXXXX/root";
    sprintf(template_text, "/proc/self/fd/%i/root", dir);
    if (-1 == symlink("/", template_text)) {
        perror("symlink");
    }

    return 0;
}

The calls to symlink and symlinkat in this case give ENOENT errors. This might also be a small oversight which is probably impossible to change now. I wonder if the code thinks this is a dangling symbolic link (a case which is mentioned by the documentation)?

The POSIX standard issue 7 also seems to miss this corner case (although it might simply be an omission of mentioning a system dependant case rather than a bug for the POSIX standard).

I'm not sure how exactly one would explain this corner case in the documentation.
Comment 1 Steven Stewart-Gallus 2014-03-31 19:22:39 UTC
This problem also exists with link and linkat.
Comment 2 Michael Kerrisk 2014-05-02 13:11:30 UTC
Steven,

(In reply to Steven Stewart-Gallus from comment #0)
> The errors printed out by the following code on symlink and symlinkat are
> missed by the documentation.
> 
> #include <fcntl.h>
> #include <stdio.h>
> #include <sys/stat.h>
> #include <sys/types.h>
> #include <unistd.h>
> 
> int main() {
>     if (-1 == mkdir("/tmp/temporary", S_IRWXU)) {
>         perror("mkdir");
>     }
> 
>     int dir = open("/tmp/temporary", O_RDONLY | O_CLOEXEC);
>     if (-1 == dir) {
>         perror("open");
>     }
> 
>     if (-1 == rmdir("/tmp/temporary")) {
>         perror("rmdir");
>     }
> 
>     if (-1 == symlinkat("/", dir, "root")) {
>         perror("symlinkat");
>     }

So far, so good.

>     char template_text[] = "/proc/self/fd/XXXXXXXXXXX/root";
>     sprintf(template_text, "/proc/self/fd/%i/root", dir);
>     if (-1 == symlink("/", template_text)) {
>         perror("symlink");
>     }

I'm a little puzzled, why would one want to make a symlink somewhere under /proc?

> 
>     return 0;
> }
> 
> The calls to symlink and symlinkat in this case give ENOENT errors. 

For symlinkat(), I can see the logic. You're trying to create a link relative to a directory that does not exist. Could the call reasonably do anything other than fail?

> This
> might also be a small oversight which is probably impossible to change now.
> I wonder if the code thinks this is a dangling symbolic link (a case which
> is mentioned by the documentation)?
> 
> The POSIX standard issue 7 also seems to miss this corner case (although it
> might simply be an omission of mentioning a system dependant case rather
> than a bug for the POSIX standard).

The specs in POSIX were driven from the Linux implementation effort, so I don;t think that your hypothesis there would fit.

> I'm not sure how exactly one would explain this corner case in the
> documentation.

I'd be willing to give it a shot, but, first,I'm not sure what if anything to say about the symlink() case. Do you have a more realistic example of that case?
Comment 3 Steven Stewart-Gallus 2014-05-02 18:51:33 UTC
On further reflection, this is not a problem with the documentation of
symlink, symlinkat, link and linkat but a problem with the
documentation of ENOENT and what a nonexisting directory actually
means.

I suppose how one might describe the situation is that when a
directory is unlinked from a filesystem (but still has process
references to it) it enters a zombie state where many filesystem calls
become inappropriate to use on it.

I'm not sure where such documentation should be placed. We already
have shm_overview(7), mq_overview(7) and such; and I think if we had a
dir_overview(7) that would be the perfect place to put this corner
case. dir_overview(7) might be too big project for not really any gain
though.

> I'd be willing to give it a shot, but, first,I'm not sure what if
> anything to say about the symlink() case. Do you have a more
> realistic example of that case?

Nope. People should probably be able to figure that out from the
description of the symlink case. I was just pointing it out for
completeness and I'd be fine if that case was omitted.
Comment 4 Michael Kerrisk 2014-05-05 11:06:49 UTC
I've applied the patches below.

diff --git a/man2/link.2 b/man2/link.2
index d5a697d..df2b9e2 100644
--- a/man2/link.2
+++ b/man2/link.2
@@ -291,6 +291,17 @@ file corresponding to a file descriptor created with
 See
 .BR open (2).
 .TP
+.B ENOENT
+.I oldpath
+is a relative pathname and
+.I olddirfd
+refers to a directory that has been deleted,
+or
+.I newpath
+is a relative pathname and
+.I newdirfd
+refers to a directory that has been deleted.
+.TP
 .B ENOTDIR
 .I oldpath
 is relative and

diff --git a/man2/symlink.2 b/man2/symlink.2
index a6cf6a7..8bd067f 100644
--- a/man2/symlink.2
+++ b/man2/symlink.2
@@ -210,6 +210,12 @@ The following additional errors can occur for
 .I newdirfd
 is not a valid file descriptor.
 .TP
+.B ENOENT
+.I linkpath
+is a relative pathname and
+.IR newdirfd
+refers to a directory that has been deleted.
+.TP
 .B ENOTDIR
 .I linkpath
 is relative and

Note You need to log in before you can comment on or make changes to this bug.