summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/cli/CLI.cpp74
-rw-r--r--src/daemon/abrt-server.c1
-rw-r--r--src/hooks/abrt-hook-ccpp.c1
-rw-r--r--src/include/abrtlib.h2
-rw-r--r--src/include/report/dump_dir.h4
-rw-r--r--src/lib/Makefile.am1
-rw-r--r--src/lib/copy_file_recursive.c139
-rw-r--r--src/lib/create_dump_dir.c2
-rw-r--r--src/lib/dump_dir.c50
-rw-r--r--src/plugins/abrt-dump-oops.c1
10 files changed, 259 insertions, 16 deletions
diff --git a/src/cli/CLI.cpp b/src/cli/CLI.cpp
index 9af1cbc7..af85a9e9 100644
--- a/src/cli/CLI.cpp
+++ b/src/cli/CLI.cpp
@@ -38,7 +38,7 @@ static char *localize_crash_time(const char *timestr)
static crash_data_t *FillCrashInfo(const char *dump_dir_name)
{
- struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ DD_OPEN_READONLY);
if (!dd)
return NULL;
@@ -199,6 +199,46 @@ static void print_crash_info(crash_data_t *crash_data, bool show_backtrace)
}
}
+static struct dump_dir *steal_directory(const char *base_dir, const char *dump_dir_name)
+{
+ const char *base_name = strrchr(dump_dir_name, '/');
+ if (base_name)
+ base_name++;
+ else
+ base_name = dump_dir_name;
+
+ struct dump_dir *dd_dst;
+ unsigned count = 100;
+ char *dst_dir_name = concat_path_file(base_dir, base_name);
+ while (1)
+ {
+ dd_dst = dd_create(dst_dir_name, (uid_t)-1);
+ free(dst_dir_name);
+ if (dd_dst)
+ break;
+ if (--count == 0)
+ {
+ error_msg("Can't create new dump dir in '%s'", base_dir);
+ goto ret;
+ }
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ dst_dir_name = xasprintf("%s/%s.%u", base_dir, base_name, (int)tv.tv_usec);
+ }
+
+ log("Creating copy in '%s'", dd_dst->dd_dir);
+ if (copy_file_recursive(dump_dir_name, dd_dst->dd_dir) < 0)
+ {
+ /* error. copy_file_recursive already emitted error message */
+ dd_close(dd_dst);
+ xfunc_die();
+ }
+
+ ret:
+ return dd_dst;
+}
+
+
/* Program options */
enum
{
@@ -329,6 +369,14 @@ int main(int argc, char** argv)
}
end_of_arg_parsing: ;
+ if (!D_list)
+ {
+ char *home = getenv("HOME");
+ if (home)
+ D_list = g_list_append(D_list, concat_path_file(home, "abrt/spool"));
+ D_list = g_list_append(D_list, (void*)DEBUG_DUMPS_DIR);
+ }
+
/* Handle option arguments. */
argc -= optind;
switch (argc)
@@ -368,13 +416,6 @@ int main(int argc, char** argv)
{
case OPT_GET_LIST:
{
- if (!D_list)
- {
- char *home = getenv("HOME");
- if (home)
- D_list = g_list_append(D_list, concat_path_file(home, "abrt/spool"));
- D_list = g_list_append(D_list, (void*)DEBUG_DUMPS_DIR);
- }
vector_of_crash_data_t *ci = new_vector_of_crash_data();
while (D_list)
{
@@ -388,6 +429,23 @@ int main(int argc, char** argv)
}
case OPT_REPORT:
{
+ struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY);
+ if (!dd)
+ break;
+ int readonly = !dd->locked;
+ dd_close(dd);
+ if (readonly)
+ {
+ log("'%s' is not writable", dump_dir_name);
+ /* D_list can't be NULL here */
+ struct dump_dir *dd_copy = steal_directory((char *)D_list->data, dump_dir_name);
+ if (dd_copy)
+ {
+ dump_dir_name = xstrdup(dd_copy->dd_dir);
+ dd_close(dd_copy);
+ }
+ }
+
exitcode = report(dump_dir_name, (always ? CLI_REPORT_BATCH : 0));
if (exitcode == -1)
error_msg_and_die("Crash '%s' not found", dump_dir_name);
diff --git a/src/daemon/abrt-server.c b/src/daemon/abrt-server.c
index 82d69670..f34f3366 100644
--- a/src/daemon/abrt-server.c
+++ b/src/daemon/abrt-server.c
@@ -119,6 +119,7 @@ static void create_debug_dump()
{
error_msg_and_die("Error creating crash dump %s", path);
}
+ dd_create_basic_files(dd, client_uid);
dd_save_text(dd, FILENAME_ANALYZER, analyzer);
dd_save_text(dd, FILENAME_EXECUTABLE, executable);
diff --git a/src/hooks/abrt-hook-ccpp.c b/src/hooks/abrt-hook-ccpp.c
index c9327723..dc95d6dc 100644
--- a/src/hooks/abrt-hook-ccpp.c
+++ b/src/hooks/abrt-hook-ccpp.c
@@ -497,6 +497,7 @@ int main(int argc, char** argv)
struct dump_dir *dd = dd_create(path, uid);
if (dd)
{
+ dd_create_basic_files(dd, uid);
char *cmdline = get_cmdline(pid); /* never NULL */
char *reason = xasprintf("Process %s was killed by signal %s (SIG%s)", executable, signal_str, signame ? signame : signal_str);
dd_save_text(dd, FILENAME_ANALYZER, "CCpp");
diff --git a/src/include/abrtlib.h b/src/include/abrtlib.h
index 22b63baf..bb3dddc2 100644
--- a/src/include/abrtlib.h
+++ b/src/include/abrtlib.h
@@ -123,6 +123,8 @@ off_t copyfd_size(int src_fd, int dst_fd, off_t size, int flags);
void copyfd_exact_size(int src_fd, int dst_fd, off_t size);
#define copy_file abrt_copy_file
off_t copy_file(const char *src_name, const char *dst_name, int mode);
+#define copy_file_recursive abrt_copy_file_recursive
+int copy_file_recursive(const char *source, const char *dest);
/* Returns malloc'ed block */
#define encode_base64 abrt_encode_base64
diff --git a/src/include/report/dump_dir.h b/src/include/report/dump_dir.h
index 4f2914d0..3bcc526d 100644
--- a/src/include/report/dump_dir.h
+++ b/src/include/report/dump_dir.h
@@ -31,6 +31,7 @@ extern "C" {
enum {
DD_FAIL_QUIETLY = (1 << 0),
+ DD_OPEN_READONLY = (1 << 1),
};
struct dump_dir {
@@ -48,13 +49,14 @@ struct dump_dir *dd_opendir(const char *dir, int flags);
* (IOW: if you aren't running under root):
*/
struct dump_dir *dd_create(const char *dir, uid_t uid);
+void dd_create_basic_files(struct dump_dir *dd, uid_t uid);
int dd_exist(struct dump_dir *dd, const char *path);
DIR *dd_init_next_file(struct dump_dir *dd);
int dd_get_next_file(struct dump_dir *dd, char **short_name, char **full_name);
enum {
/* DD_FAIL_QUIETLY bit is valid for dd_load_text_ext too, */
- DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE = (1 << 1),
+ DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE = (1 << 2),
};
char* dd_load_text_ext(const struct dump_dir *dd, const char *name, unsigned flags);
char* dd_load_text(const struct dump_dir *dd, const char *name);
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 89cad8ab..aece0533 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -20,6 +20,7 @@ libreport_la_SOURCES = \
read_write.c read_write.h \
logging.c logging.h \
copyfd.c \
+ copy_file_recursive.c \
concat_path_file.c \
append_to_malloced_string.c \
overlapping_strcpy.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;
+}
diff --git a/src/lib/create_dump_dir.c b/src/lib/create_dump_dir.c
index 652a16c6..2caf9af9 100644
--- a/src/lib/create_dump_dir.c
+++ b/src/lib/create_dump_dir.c
@@ -23,6 +23,8 @@ static struct dump_dir *try_dd_create(const char *base_dir_name, const char *dir
{
char *path = concat_path_file(base_dir_name, dir_name);
struct dump_dir *dd = dd_create(path, (uid_t)-1L);
+ if (dd)
+ dd_create_basic_files(dd, (uid_t)-1L);
free(path);
return dd;
}
diff --git a/src/lib/dump_dir.c b/src/lib/dump_dir.c
index 6b7fc6df..cacedaee 100644
--- a/src/lib/dump_dir.c
+++ b/src/lib/dump_dir.c
@@ -282,8 +282,21 @@ struct dump_dir *dd_opendir(const char *dir, int flags)
dir = dd->dd_dir = rm_trailing_slashes(dir);
+ errno = 0;
if (dd_lock(dd, WAIT_FOR_OTHER_PROCESS_USLEEP) < 0)
{
+ if ((flags & DD_OPEN_READONLY) && errno == EACCES)
+ {
+ /* Directory is not writable. If it seems to be readable,
+ * return "read only" dd, not NULL */
+ struct stat stat_buf;
+ if (stat(dir, &stat_buf) == 0
+ && S_ISDIR(stat_buf.st_mode)
+ && access(dir, R_OK) == 0
+ ) {
+ return dd;
+ }
+ }
if (errno == EISDIR)
{
/* EISDIR: dd_lock can lock the dir, but it sees no time file there,
@@ -378,12 +391,31 @@ struct dump_dir *dd_create(const char *dir, uid_t uid)
return NULL;
}
+ bool created_parents = false;
+ try_again:
/* Was creating it with mode 0700 and user as the owner, but this allows
* the user to replace any file in the directory, changing security-sensitive data
* (e.g. "uid", "analyzer", "executable")
*/
if (mkdir(dir, 0750) == -1)
{
+ int err = errno;
+ if (!created_parents && errno == ENOENT)
+ {
+ char *p = dd->dd_dir + 1;
+ while ((p = strchr(p, '/')) != NULL)
+ {
+ *p = '\0';
+ int r = (mkdir(dd->dd_dir, 0755) == 0 || errno == EEXIST);
+ *p++ = '/';
+ if (!r)
+ goto report_err;
+ }
+ created_parents = true;
+ goto try_again;
+ }
+ report_err:
+ errno = err;
perror_msg("Can't create directory '%s'", dir);
dd_close(dd);
return NULL;
@@ -430,12 +462,19 @@ struct dump_dir *dd_create(const char *dir, uid_t uid)
}
}
+ return dd;
+}
+
+void dd_create_basic_files(struct dump_dir *dd, uid_t uid)
+{
char long_str[sizeof(long) * 3 + 2];
time_t t = time(NULL);
sprintf(long_str, "%lu", (long)t);
dd_save_text(dd, FILENAME_TIME, long_str);
+ if (uid == (uid_t)-1)
+ uid = getuid();
sprintf(long_str, "%lu", (long)uid);
dd_save_text(dd, FILENAME_UID, long_str);
@@ -448,11 +487,8 @@ struct dump_dir *dd_create(const char *dir, uid_t uid)
DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE);
if (!release)
release = load_text_file("/etc/redhat-release", /*flags:*/ 0);
- strchrnul(release, '\n')[0] = '\0';
dd_save_text(dd, FILENAME_RELEASE, release);
free(release);
-
- return dd;
}
static int delete_file_dir(const char *dir, bool skip_lock_file)
@@ -608,8 +644,8 @@ static bool save_binary_file(const char *path, const char* data, unsigned size,
char* dd_load_text_ext(const struct dump_dir *dd, const char *name, unsigned flags)
{
- if (!dd->locked)
- error_msg_and_die("dump_dir is not opened"); /* bug */
+// if (!dd->locked)
+// error_msg_and_die("dump_dir is not opened"); /* bug */
char *full_path = concat_path_file(dd->dd_dir, name);
char *ret = load_text_file(full_path, flags);
@@ -645,8 +681,8 @@ void dd_save_binary(struct dump_dir* dd, const char* name, const char* data, uns
DIR *dd_init_next_file(struct dump_dir *dd)
{
- if (!dd->locked)
- error_msg_and_die("dump_dir is not opened"); /* bug */
+// if (!dd->locked)
+// error_msg_and_die("dump_dir is not opened"); /* bug */
if (dd->next_dir)
closedir(dd->next_dir);
diff --git a/src/plugins/abrt-dump-oops.c b/src/plugins/abrt-dump-oops.c
index bf4f1e96..5b6a2062 100644
--- a/src/plugins/abrt-dump-oops.c
+++ b/src/plugins/abrt-dump-oops.c
@@ -506,6 +506,7 @@ static int save_oops_to_dump_dir(GList *oops_list, unsigned oops_cnt)
struct dump_dir *dd = dd_create(path, /*uid:*/ 0);
if (dd)
{
+ dd_create_basic_files(dd, /*uid:*/ 0);
dd_save_text(dd, FILENAME_ANALYZER, "Kerneloops");
dd_save_text(dd, FILENAME_EXECUTABLE, "kernel");
dd_save_text(dd, FILENAME_KERNEL, first_line);