/* Copyright (C) 2020 CZ.NIC, z.s.p.o. 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 . */ #include #include #include #include #include #include #include #include #include "contrib/files.h" #include "contrib/string.h" #include "libknot/errcode.h" #if defined(MAXBSIZE) #define BUFSIZE MAXBSIZE #else #define BUFSIZE (64 * 1024) #endif static int remove_file(const char *path, const struct stat *stat, int type, struct FTW *ftw) { (void)stat; (void)ftw; if (type == FTW_DP) { return rmdir(path); } else { return unlink(path); } } bool remove_path(const char *path) { return (0 == nftw(path, remove_file, 1, FTW_DEPTH | FTW_PHYS)); } int make_dir(const char *path, mode_t mode, bool ignore_existing) { if (mkdir(path, mode) == 0) { return KNOT_EOK; } if (!ignore_existing || errno != EEXIST) { return knot_map_errno(); } assert(errno == EEXIST); struct stat st = { 0 }; if (stat(path, &st) != 0) { return knot_map_errno(); } if (!S_ISDIR(st.st_mode)) { return knot_map_errno_code(ENOTDIR); } return KNOT_EOK; } int make_path(const char *path, mode_t mode) { if (path == NULL) { return KNOT_EINVAL; } char *dir = strdup(path); if (dir == NULL) { return KNOT_ENOMEM; } for (char *p = strchr(dir + 1, '/'); p != NULL; p = strchr(p + 1, '/')) { *p = '\0'; if (mkdir(dir, mode) == -1 && errno != EEXIST) { free(dir); return knot_map_errno(); } *p = '/'; } free(dir); return KNOT_EOK; } int open_tmp_file(const char *path, char **tmp_name, FILE **file, mode_t mode) { int ret; *tmp_name = sprintf_alloc("%s.XXXXXX", path); if (*tmp_name == NULL) { ret = KNOT_ENOMEM; goto open_tmp_failed; } int fd = mkstemp(*tmp_name); if (fd < 0) { ret = knot_map_errno(); goto open_tmp_failed; } if (fchmod(fd, mode) != 0) { ret = knot_map_errno(); close(fd); unlink(*tmp_name); goto open_tmp_failed; } *file = fdopen(fd, "w"); if (*file == NULL) { ret = knot_map_errno(); close(fd); unlink(*tmp_name); goto open_tmp_failed; } return KNOT_EOK; open_tmp_failed: free(*tmp_name); *tmp_name = NULL; *file = NULL; assert(ret != KNOT_EOK); return ret; } int copy_file(const char *dest, const char *src) { if (dest == NULL || src == NULL) { return KNOT_EINVAL; } int ret = 0; char *buf = NULL, *tmp_name = NULL; FILE *file = NULL; FILE *from = fopen(src, "r"); if (from == NULL) { ret = knot_map_errno(); goto done; } buf = malloc(sizeof(*buf) * BUFSIZE); if (buf == NULL) { ret = KNOT_ENOMEM; goto done; } ret = open_tmp_file(dest, &tmp_name, &file, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); if (ret != KNOT_EOK) { goto done; } ssize_t cnt; while ((cnt = fread(buf, sizeof(*buf), BUFSIZE, from)) != 0 && (ret = (fwrite(buf, sizeof(*buf), cnt, file) == cnt))) { } ret = !ret || ferror(from); if (ret != 0) { ret = knot_map_errno(); unlink(tmp_name); goto done; } ret = rename(tmp_name, dest); if (ret != 0) { ret = knot_map_errno(); unlink(tmp_name); goto done; } ret = KNOT_EOK; done: free(tmp_name); if (file != NULL) { fclose(file); } free(buf); if (from != NULL) { fclose(from); } return ret; }