Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

how to get unlinkat(dir_fd, ".", AT_REMOVEDIR) to work?

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading