#include <stddef.h> /* for offsetof() */
#include <stdlib.h> /* for exit() */
#include <stdio.h> /* for perror() */
#include <string.h> /* for strcmp() */
#include <unistd.h> /* for close(), unlink() */
#include <fcntl.h> /* for open() */
#include <dirent.h> /* for DIR */
#include <sys/stat.h> /* for stat */
#include <limits.h> /* for NAME_MAX */
int rm_rf(const char* path)
{
if (unlink(path) == 0) {
return 0;
} else {
int dirfd = open(path, O_RDONLY | O_DIRECTORY);
if (dirfd == -1) {
perror("open");
return -1;
}
if (rm_children(dirfd) == -1) {
return -1;
}
if (rmdir(path) == -1) {
perror("rmdir");
return -1;
}
return 0;
}
}
int rm_children(int dirfd)
{
DIR* dir = fdopendir(dirfd);
if (dir == NULL) {
perror("fdopendir");
if (close(dirfd) == -1) {
perror("close");
}
return -1;
}
char buf[offsetof(struct dirent, d_name) + NAME_MAX + 1];
struct dirent* dbuf = (struct dirent*)buf;
struct dirent* dent = NULL;
int ret = 0;
for (;;) {
if ((ret = readdir_r(dir, dbuf, &dent)) == -1) {
perror("readdir_r");
break;
}
if (dent == NULL) {
break;
}
if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
continue;
}
if ((ret = rm_at(dirfd, dent->d_name)) == -1) {
break;
}
}
if (closedir(dir) == -1) {
perror("closedir");
ret = -1;
}
return ret;
}
int rm_at(int dirfd, const char* name)
{
int fd = openat(dirfd, name, O_RDONLY);
if (fd == -1) {
perror("openat");
return -1;
}
int ret = 0;
struct stat st;
if ((ret = fstat(fd, &st)) == -1) {
perror("fstat");
goto out;
}
if (S_ISDIR(st.st_mode)) {
ret = rm_children(fd);
fd = -1;
if (ret == -1) {
goto out;
}
}
ret = unlinkat(dirfd, name, S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0);
if (ret == -1) {
perror("unlinkat");
goto out;
}
out:
if (fd != -1) {
if (close(fd) == -1) {
perror("close");
ret = -1;
}
}
return ret;
}
Note: this method is thread-safe, but uses stack for uncontrolled recursion. Each tree level adds at least NAME_MAX bytes + one frame size.