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.
This problem also exists with link and linkat.
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?
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.
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