I need to unlink an empty directory with
unlinkat(dir_fd, ".", AT_REMOVEDIR)
and I am getting EINVAL errno, which, according to official GNU documentation, must mean "An invalid flag value was specified in flags", which is not the case, as AT_REMOVEDIR is (the only) allowed flag.
SSCCE is as follows. Note, that in the simplest code, I do have the directory name, so I could have used rmdir. In the real situation, I don’t have the name, I only have the descriptor, to an empty directory, and I need to remove it – so I have to use unlinkat .
foobar.c:
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
int dir_fd =
open("dir", O_RDONLY | O_PATH | O_DIRECTORY);
if (-1 == dir_fd) {
perror("open");
return(1);
}
if (unlinkat(dir_fd, ".", AT_REMOVEDIR)) {
perror("unlinkat");
fprintf(stderr, "errno %d\n", errno);
return(1);
}
}
and I get this behaviour:
$mkdir dir
$gcc -lc foobar.c
$./a.out
unlinkat: Invalid argument
errno 22
$errno 22
EINVAL 22 Invalid argument
and GNU documentation for unlinkat says
EINVAL An invalid flag value was specified in flags.
>Solution :
When called with AT_REMOVEDIR, unlinkat(2) behaves as rmdir(2), and ultimately fails to the same reasons.
For rmdir(2):
EINVAL pathname has . as last component.
You have seemingly flipped the interface, really it should be used as
/* foobar.c */
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int dir_fd = open(".", O_RDONLY | O_PATH | O_DIRECTORY);
if (-1 == dir_fd) {
perror("open");
return(1);
}
if (unlinkat(dir_fd, "dir", AT_REMOVEDIR)) {
perror("unlinkat");
fprintf(stderr, "errno %d\n", errno);
return(1);
}
close(dir_fd);
}
$ mkdir dir
$ ./a.out
(Or the special value of AT_FDCWD should be used for the first argument of unlinkat to remove a file relative to the current working directory.)
This obviously does not help you if you do not know the name of the directory to be removed
See: Retrieve filename from file descriptor in C