/
githubmirror
/
authselect
Обзор
Документация
Войти
/
githubmirror
/
authselect
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/lib/util/dir.c
339 строк
7 KB
Pavel Březina
lib: fix coverity warnings
13 июн 2019, 12:01
13 июн 2019, 12:01
35292b8
Код
Авторство
О чём код?
/* Authors: Pavel Březina <pbrezina@redhat.com> Copyright (C) 2019 Red Hat 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 "config.h" #include <errno.h> #include <fcntl.h> #include <dirent.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include <sys/stat.h> #include <unistd.h> #include "common/common.h" #include "lib/util/dir.h" #include "lib/util/file.h" #include "lib/util/string_array.h" static int compare_timespec(struct timespec *a, struct timespec *b) { if (a->tv_sec == b->tv_sec) { if (a->tv_nsec < b->tv_nsec) { return -1; } if (a->tv_nsec > b->tv_nsec) { return 1; } return 0; } if (a->tv_sec < b->tv_sec) { return -1; } if (a->tv_sec > b->tv_sec) { return 1; } return 0; } static int sort_by_ctime(const void *a, const void *b, void *arg) { const char *name_a = *(const char **)a; const char *name_b = *(const char **)b; int dirfd = *(int *)arg; const char *base_a; const char *base_b; struct stat stat_a; struct stat stat_b; errno_t ret; base_a = file_get_basename(name_a); if (base_a == NULL) { ERROR("Unable to get basename of [%s]", name_a); return 0; } base_b = file_get_basename(name_b); if (base_b == NULL) { ERROR("Unable to get basename of [%s]", name_b); return 0; } ret = fstatat(dirfd, base_a, &stat_a, 0); if (ret != 0) { ret = errno; ERROR("Unable to stat [%s] [%d]: %s", name_a, ret, strerror(ret)); return 0; } ret = fstatat(dirfd, base_b, &stat_b, 0); if (ret != 0) { ret = errno; ERROR("Unable to stat [%s] [%d]: %s", name_b, ret, strerror(ret)); return 0; } return compare_timespec(&stat_a.st_ctim, &stat_b.st_ctim); } static bool is_dot_dir(const char *name) { return strcmp(name, ".") == 0 || strcmp(name, "..") == 0; } static bool is_directory(struct dirent *entry, int dirfd) { struct stat statres; errno_t ret; #ifdef _DIRENT_HAVE_D_TYPE if (entry->d_type == DT_DIR) { return true; } else if (entry->d_type != DT_UNKNOWN) { return false; } #endif /* We must use stat() if d_type is not available or it couldn't determine * the type (which may happen on some filesystems). */ ret = fstatat(dirfd, entry->d_name, &statres, 0); if (ret != 0) { ret = errno; ERROR("Unable to stat directory [%d]: %s", ret, strerror(ret)); return false; } if (S_ISDIR(statres.st_mode)) { return true; } return false; } static errno_t dir_open(const char *path, DIR **_dirstream, int *_descriptor) { DIR *dirstream; int descriptor; errno_t ret; if (path == NULL) { return EINVAL; } dirstream = opendir(path); if (dirstream == NULL) { ret = errno; /* To silence static analyzers that assumes that errno can be 0 here. */ if (ret == EOK) { ret = EINVAL; } return ret; } /* Descriptor is closed when closedir() is called. */ descriptor = dirfd(dirstream); if (descriptor == -1) { ret = errno; /* To silence static analyzers that assumes that errno can be 0 here. */ if (ret == EOK) { ret = EINVAL; } closedir(dirstream); return ret; } *_dirstream = dirstream; *_descriptor = descriptor; return EOK; } errno_t dir_list(const char *path, uint32_t flags, char ***_items, int *_dirfd) { struct dirent *entry; DIR *dirstream; char *fullpath; char **items; int dirfd; int dupfd; errno_t ret; ret = dir_open(path, &dirstream, &dirfd); if (ret != EOK) { return ret; } items = string_array_create(1); if (items == NULL) { ret = ENOMEM; goto done; } errno = 0; while ((entry = readdir(dirstream)) != NULL) { if (is_dot_dir(entry->d_name)) { continue; } if (is_directory(entry, dirfd)) { if (!(flags & DIR_LIST_DIRS)) { continue; } } else { if (!(flags & DIR_LIST_FILES)) { continue; } } if (flags & DIR_LIST_FULL_PATH) { fullpath = format("%s/%s", path, entry->d_name); if (fullpath == NULL) { ret = ENOMEM; goto done; } items = string_array_add_value(items, fullpath, true); free(fullpath); } else { items = string_array_add_value(items, entry->d_name, true); } if (items == NULL) { ret = ENOMEM; goto done; } } if (flags & DIR_LIST_SORT_BY_CTIME) { qsort_r(items, string_array_count(items), sizeof(char *), sort_by_ctime, &dirfd); } if (_dirfd != NULL) { dupfd = dup(dirfd); if (dupfd == -1) { ret = errno; /* To silence static analyzers that assumes that errno * can be 0 here. */ if (ret == EOK) { ret = EINVAL; } goto done; } *_dirfd = dupfd; } *_items = items; ret = EOK; done: if (ret == EOK && _dirfd != NULL) { closedir(dirstream); } if (ret != EOK) { string_array_free(items); } return ret; } errno_t dir_remove(const char *path) { char **subdirs = NULL; char **files = NULL; int dirfd = -1; errno_t ret; int i; ret = dir_list(path, DIR_LIST_DIRS | DIR_LIST_FULL_PATH, &subdirs, NULL); if (ret == ENOENT) { ret = EOK; goto done; } else if (ret != EOK) { goto done; } ret = dir_list(path, DIR_LIST_FILES, &files, &dirfd); if (ret == ENOENT) { ret = EOK; goto done; } else if (ret != EOK) { goto done; } for (i = 0; subdirs[i] != NULL; i++) { ret = dir_remove(subdirs[i]); if (ret != EOK) { goto done; } } for (i = 0; files[i] != NULL; i++) { INFO("Removing file [%s/%s]", path, files[i]); ret = unlinkat(dirfd, files[i], 0); if (ret != 0) { ret = errno; goto done; } } INFO("Removing directory [%s]", path); ret = rmdir(path); if (ret != 0) { ret = errno; goto done; } ret = EOK; done: string_array_free(subdirs); string_array_free(files); if (dirfd != -1) { close(dirfd); } return ret; }