From 55ae0eaea8684aa78089bbd0c2116e0c8cb25585 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 1 Feb 2011 16:04:30 +0100 Subject: abrt-cli -r DIR: copy non-writable DIR into $HOME/abrt/spool Signed-off-by: Denys Vlasenko --- src/lib/copy_file_recursive.c | 139 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 src/lib/copy_file_recursive.c (limited to 'src/lib/copy_file_recursive.c') diff --git a/src/lib/copy_file_recursive.c b/src/lib/copy_file_recursive.c new file mode 100644 index 00000000..c3f021c7 --- /dev/null +++ b/src/lib/copy_file_recursive.c @@ -0,0 +1,139 @@ +/* + Copyright (C) 2011 ABRT team + Copyright (C) 2011 RedHat Inc + + 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "abrtlib.h" + +int copy_file_recursive(const char *source, const char *dest) +{ + /* This is a recursive function, try to minimize stack usage */ + /* NB: each struct stat is ~100 bytes */ + struct stat source_stat; + struct stat dest_stat; + int retval = 0; + int dest_exists = 0; + + if (strcmp(source, ".lock") == 0) + goto skip; + + if (stat(source, &source_stat) < 0) { + perror_msg("Can't stat '%s'", source); + return -1; + } + + if (lstat(dest, &dest_stat) < 0) { + if (errno != ENOENT) { + perror_msg("Can't stat '%s'", dest); + return -1; + } + } else { + if (source_stat.st_dev == dest_stat.st_dev + && source_stat.st_ino == dest_stat.st_ino + ) { + error_msg("'%s' and '%s' are the same file", source, dest); + return -1; + } + dest_exists = 1; + } + + if (S_ISDIR(source_stat.st_mode)) { + DIR *dp; + struct dirent *d; + + if (dest_exists) { + if (!S_ISDIR(dest_stat.st_mode)) { + error_msg("Target '%s' is not a directory", dest); + return -1; + } + /* race here: user can substitute a symlink between + * this check and actual creation of files inside dest */ + } else { + /* Create DEST */ + mode_t mode = source_stat.st_mode; + /* Allow owner to access new dir (at least for now) */ + mode |= S_IRWXU; + if (mkdir(dest, mode) < 0) { + perror_msg("Can't create directory '%s'", dest); + return -1; + } + } + /* Recursively copy files in SOURCE */ + dp = opendir(source); + if (dp == NULL) { + retval = -1; + goto ret; + } + + while (retval == 0 && (d = readdir(dp)) != NULL) { + char *new_source, *new_dest; + + if (dot_or_dotdot(d->d_name)) + continue; + new_source = concat_path_file(source, d->d_name); + new_dest = concat_path_file(dest, d->d_name); + if (copy_file_recursive(new_source, new_dest) < 0) + retval = -1; + free(new_source); + free(new_dest); + } + closedir(dp); + + goto ret; + } + + if (S_ISREG(source_stat.st_mode)) { + int src_fd; + int dst_fd; + mode_t new_mode; + + src_fd = open(source, O_RDONLY); + if (src_fd < 0) { + perror_msg("Can't open '%s'", source); + return -1; + } + + /* Do not try to open with weird mode fields */ + new_mode = source_stat.st_mode; + + // security problem versus (sym)link attacks + // dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); + /* safe way: */ + dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); + if (dst_fd < 0) { + close(src_fd); + return -1; + } + + if (copyfd_eof(src_fd, dst_fd, COPYFD_SPARSE) == -1) + retval = -1; + close(src_fd); + /* Careful: do check that buffered writes succeeded... */ + if (close(dst_fd) < 0) { + perror_msg("Error writing to '%s'", dest); + retval = -1; + } + goto ret; + } + + /* Neither dir not regular file: skip */ + + skip: + log("Skipping '%s'", source); + ret: + return retval; +} -- cgit