Commit 68f6d0fb authored by Dominik Taborsky's avatar Dominik Taborsky

contrib: add file&directory removal function, use safer readdir

parent 87f9fdaa
......@@ -16,16 +16,9 @@
#include "files.h"
#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include "../../src/contrib/files.c"
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static char *make_temp(bool is_directory)
{
......@@ -66,68 +59,7 @@ char *test_mkdtemp(void)
return make_temp(true);
}
static bool special_name(const char *name)
{
return strcmp(name, ".") == 0 || strcmp(name, "..") == 0;
}
static bool rm_dir_contents(int dir_fd)
{
DIR *dir = fdopendir(dir_fd);
if (!dir) {
return false;
}
bool success = true;
struct dirent entry = { 0 };
struct dirent *result = NULL;
while (success && readdir_r(dir, &entry, &result) == 0 && result) {
if (special_name(result->d_name)) {
continue;
}
bool is_dir = result->d_type == DT_DIR;
if (is_dir) {
int sub = openat(dir_fd, result->d_name, O_NOFOLLOW);
success = rm_dir_contents(sub);
close(sub);
}
if (success) {
int flags = is_dir ? AT_REMOVEDIR : 0;
success = unlinkat(dir_fd, result->d_name, flags) == 0;
}
}
closedir(dir);
return success;
}
bool test_rm_rf(const char *path)
{
if (!path) {
return false;
}
int fd = open(path, O_NOFOLLOW);
if (fd < 0) {
return false;
}
struct stat st = { 0 };
if (fstat(fd, &st) != 0) {
close(fd);
return false;
}
if (S_ISDIR(st.st_mode) && !rm_dir_contents(fd)) {
close(fd);
return false;
}
close(fd);
return (remove(path) == 0);
return remove_path(path);
}
......@@ -38,5 +38,7 @@ char *test_mkdtemp(void);
/*!
* \brief Delete file or directory (recursive).
*
* \return true on success, false when one or more files failed to be removed.
*/
bool test_rm_rf(const char *path);
......@@ -42,6 +42,8 @@ libcontrib_la_SOURCES = \
contrib/base64.c \
contrib/base64.h \
contrib/endian.h \
contrib/files.c \
contrib/files.h \
contrib/getline.c \
contrib/getline.h \
contrib/hhash.c \
......
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "files.h"
#include <dirent.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
static bool special_name(const char *name)
{
return strcmp(name, ".") == 0 || strcmp(name, "..") == 0;
}
static bool rm_dir_contents(int dir_fd)
{
DIR *dir = fdopendir(dir_fd);
if (!dir) {
return false;
}
bool success = true;
struct dirent *result = NULL;
while (success && (result = readdir(dir)) != NULL) {
if (special_name(result->d_name)) {
continue;
}
bool is_dir = result->d_type == DT_DIR;
if (is_dir) {
int sub = openat(dir_fd, result->d_name, O_NOFOLLOW);
success = rm_dir_contents(sub);
close(sub);
}
if (success) {
int flags = is_dir ? AT_REMOVEDIR : 0;
success = unlinkat(dir_fd, result->d_name, flags) == 0;
}
}
closedir(dir);
return success;
}
bool remove_path(const char *path)
{
if (!path) {
return false;
}
int fd = open(path, O_NOFOLLOW);
if (fd < 0) {
return false;
}
struct stat st = { 0 };
if (fstat(fd, &st) != 0) {
close(fd);
return false;
}
if (S_ISDIR(st.st_mode) && !rm_dir_contents(fd)) {
close(fd);
return false;
}
close(fd);
return (remove(path) == 0);
}
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdbool.h>
/*!
* \brief Delete file or directory (recursive).
*
* \return true on success, false when one or more files failed to be removed.
*/
bool remove_path(const char *path);
......@@ -14,10 +14,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <urcu.h>
#include "knot/conf/base.h"
......@@ -29,6 +25,7 @@
#include "libknot/libknot.h"
#include "libknot/yparser/ypformat.h"
#include "libknot/yparser/yptrafo.h"
#include "contrib/files.h"
#include "contrib/mempattern.h"
#include "contrib/sockaddr.h"
#include "contrib/string.h"
......@@ -41,53 +38,6 @@ conf_t* conf(void) {
return s_conf;
}
static void rm_dir(const char *path)
{
DIR *dir = opendir(path);
if (dir == NULL) {
CONF_LOG(LOG_WARNING, "failed to remove directory '%s'", path);
return;
}
// Prepare own dirent structure (see NOTES in man readdir_r).
size_t len = offsetof(struct dirent, d_name) +
fpathconf(dirfd(dir), _PC_NAME_MAX) + 1;
struct dirent *entry = malloc(len);
if (entry == NULL) {
CONF_LOG(LOG_WARNING, "failed to remove directory '%s'", path);
closedir(dir);
return;
}
memset(entry, 0, len);
// Firstly, delete all files in the directory.
int ret;
struct dirent *result = NULL;
while ((ret = readdir_r(dir, entry, &result)) == 0 &&
result != NULL) {
if (entry->d_name[0] == '.') {
continue;
}
char *file = sprintf_alloc("%s/%s", path, entry->d_name);
if (file == NULL) {
ret = KNOT_ENOMEM;
break;
}
remove(file);
free(file);
}
free(entry);
closedir(dir);
// Secondly, delete the directory if it is empty.
if (ret != 0 || remove(path) != 0) {
CONF_LOG(LOG_WARNING, "failed to remove whole directory '%s'", path);
}
}
static int init_and_check(
conf_t *conf,
conf_flag_t flags)
......@@ -219,7 +169,9 @@ int conf_new(
ret = out->api->init(&out->db, out->mm, &lmdb_opts);
// Remove the database to ensure it is temporary.
rm_dir(lmdb_opts.path);
if (!remove_path(lmdb_opts.path)) {
CONF_LOG(LOG_WARNING, "failed to purge temporary directory '%s'", lmdb_opts.path);
}
} else {
// Set the specified database.
lmdb_opts.path = db_dir;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment