Blame SOURCES/0313-ccpp-fast-dumping-and-abrt-core-limit.patch

a60cd7
From 523792cfbbaf3a5e10cdcb46979739b36ac9c4dd Mon Sep 17 00:00:00 2001
a60cd7
From: Martin Kutlak <mkutlak@redhat.com>
a60cd7
Date: Wed, 26 Sep 2018 13:24:40 +0200
a60cd7
Subject: [PATCH] ccpp: fast dumping and abrt core limit
a60cd7
a60cd7
This commit introduces a new configuration option MaxCoreFileSize. The
a60cd7
option will be compared to MaxCrashReportsSize and the minimum will be
a60cd7
used as the limit for the size of the core dump file in a dump directory.
a60cd7
a60cd7
This commit replaces the read once write twice algorithm with a tee + splice
a60cd7
algorithm. tee() does zero copy - it should just increment counters -
a60cd7
and splice() moves bytes between file descriptors without the need to
a60cd7
copy them to user space. Basically the new algorithm is the same but
a60cd7
without the need to copy data to user space. However, the original
a60cd7
algorithm was testing the buffer for 0s and if the entire buffer was 0,
a60cd7
then lseek() was performed instead of write(). The 0 check was there to
a60cd7
make the dumping faster and this is no longer needed as the tee +
a60cd7
splice is faster than the original read once write twice algorithm.
a60cd7
a60cd7
Related: rhbz#1613236
a60cd7
a60cd7
Signed-off-by: Martin Kutlak <mkutlak@redhat.com>
a60cd7
---
a60cd7
 doc/abrt-CCpp.conf.txt     |   5 +
a60cd7
 src/hooks/CCpp.conf        |  10 +
a60cd7
 src/hooks/abrt-hook-ccpp.c | 381 +++++++++++++++++++++++++------------
a60cd7
 3 files changed, 274 insertions(+), 122 deletions(-)
a60cd7
a60cd7
diff --git a/doc/abrt-CCpp.conf.txt b/doc/abrt-CCpp.conf.txt
a60cd7
index dffa45dc2..2a626ec49 100644
a60cd7
--- a/doc/abrt-CCpp.conf.txt
a60cd7
+++ b/doc/abrt-CCpp.conf.txt
a60cd7
@@ -19,6 +19,11 @@ MakeCompatCore = 'yes' / 'no' ...::
a60cd7
    instead of the template.
a60cd7
    For more information about naming core dump files see 'man 5 core'.
a60cd7
 
a60cd7
+MaxCoreFileSize = 'a number in MiB' ...::
a60cd7
+   This configuration option together with MaxCrashReportsSize set the limit on
a60cd7
+   the size of dumped core file. The lower value of the both options is used as
a60cd7
+   the effective limit. 0 is evaluated as unlimited for the both options.
a60cd7
+
a60cd7
 SaveBinaryImage = 'yes' / 'no' ...::
a60cd7
    Do you want a copy of crashed binary be saved?
a60cd7
    Useful, for example, when _deleted binary_ segfaults.
a60cd7
diff --git a/src/hooks/CCpp.conf b/src/hooks/CCpp.conf
a60cd7
index af31ed53c..48c8c2557 100644
a60cd7
--- a/src/hooks/CCpp.conf
a60cd7
+++ b/src/hooks/CCpp.conf
a60cd7
@@ -9,6 +9,16 @@
a60cd7
 # For more information about naming core dump files see 'man 5 core'.
a60cd7
 MakeCompatCore = yes
a60cd7
 
a60cd7
+# The option allows you to set limit for the core file size in MiB.
a60cd7
+#
a60cd7
+# This value is compared to value of the MaxCrashReportSize configuration
a60cd7
+# option from (/etc/abrt.conf) and the lower value is used as the limit.
a60cd7
+#
a60cd7
+# If MaxCoreFileSize is 0 then the value of MaxCrashReportSize is the limit.
a60cd7
+# If MaxCrashReportSize is 0 then the value of MaxCoreFileSize is the limit.
a60cd7
+# If both values are 0 then the core file size is unlimited.
a60cd7
+MaxCoreFileSize = 0
a60cd7
+
a60cd7
 # Do you want a copy of crashed binary be saved?
a60cd7
 # (useful, for example, when _deleted binary_ segfaults)
a60cd7
 SaveBinaryImage = no
a60cd7
diff --git a/src/hooks/abrt-hook-ccpp.c b/src/hooks/abrt-hook-ccpp.c
a60cd7
index cb4d1e0ce..ca4b61bf1 100644
a60cd7
--- a/src/hooks/abrt-hook-ccpp.c
a60cd7
+++ b/src/hooks/abrt-hook-ccpp.c
a60cd7
@@ -31,6 +31,8 @@
a60cd7
 #define  DUMP_SUID_UNSAFE 1
a60cd7
 #define  DUMP_SUID_SAFE 2
a60cd7
 
a60cd7
+#define KERNEL_PIPE_BUFFER_SIZE 65536
a60cd7
+
a60cd7
 static int g_user_core_flags;
a60cd7
 static int g_need_nonrelative;
a60cd7
 
a60cd7
@@ -54,100 +56,6 @@ static char* malloc_readlink(const char *linkname)
a60cd7
     return NULL;
a60cd7
 }
a60cd7
 
a60cd7
-/* Custom version of copyfd_xyz,
a60cd7
- * one which is able to write into two descriptors at once.
a60cd7
- */
a60cd7
-#define CONFIG_FEATURE_COPYBUF_KB 4
a60cd7
-static off_t copyfd_sparse(int src_fd, int dst_fd1, int dst_fd2, off_t size2)
a60cd7
-{
a60cd7
-	off_t total = 0;
a60cd7
-	int last_was_seek = 0;
a60cd7
-#if CONFIG_FEATURE_COPYBUF_KB <= 4
a60cd7
-	char buffer[CONFIG_FEATURE_COPYBUF_KB * 1024];
a60cd7
-	enum { buffer_size = sizeof(buffer) };
a60cd7
-#else
a60cd7
-	char *buffer;
a60cd7
-	int buffer_size;
a60cd7
-
a60cd7
-	/* We want page-aligned buffer, just in case kernel is clever
a60cd7
-	 * and can do page-aligned io more efficiently */
a60cd7
-	buffer = mmap(NULL, CONFIG_FEATURE_COPYBUF_KB * 1024,
a60cd7
-			PROT_READ | PROT_WRITE,
a60cd7
-			MAP_PRIVATE | MAP_ANON,
a60cd7
-			/* ignored: */ -1, 0);
a60cd7
-	buffer_size = CONFIG_FEATURE_COPYBUF_KB * 1024;
a60cd7
-	if (buffer == MAP_FAILED) {
a60cd7
-		buffer = alloca(4 * 1024);
a60cd7
-		buffer_size = 4 * 1024;
a60cd7
-	}
a60cd7
-#endif
a60cd7
-
a60cd7
-	while (1) {
a60cd7
-		ssize_t rd = safe_read(src_fd, buffer, buffer_size);
a60cd7
-		if (!rd) { /* eof */
a60cd7
-			if (last_was_seek) {
a60cd7
-				if (lseek(dst_fd1, -1, SEEK_CUR) < 0
a60cd7
-				 || safe_write(dst_fd1, "", 1) != 1
a60cd7
-				 || (dst_fd2 >= 0
a60cd7
-				     && (lseek(dst_fd2, -1, SEEK_CUR) < 0
a60cd7
-					 || safe_write(dst_fd2, "", 1) != 1
a60cd7
-				        )
a60cd7
-				    )
a60cd7
-				) {
a60cd7
-					perror_msg("Write error");
a60cd7
-					total = -1;
a60cd7
-					goto out;
a60cd7
-				}
a60cd7
-			}
a60cd7
-			/* all done */
a60cd7
-			goto out;
a60cd7
-		}
a60cd7
-		if (rd < 0) {
a60cd7
-			perror_msg("Read error");
a60cd7
-			total = -1;
a60cd7
-			goto out;
a60cd7
-		}
a60cd7
-
a60cd7
-		/* checking sparseness */
a60cd7
-		ssize_t cnt = rd;
a60cd7
-		while (--cnt >= 0) {
a60cd7
-			if (buffer[cnt] != 0) {
a60cd7
-				/* not sparse */
a60cd7
-				errno = 0;
a60cd7
-				ssize_t wr1 = full_write(dst_fd1, buffer, rd);
a60cd7
-				ssize_t wr2 = (dst_fd2 >= 0 ? full_write(dst_fd2, buffer, rd) : rd);
a60cd7
-				if (wr1 < rd || wr2 < rd) {
a60cd7
-					perror_msg("Write error");
a60cd7
-					total = -1;
a60cd7
-					goto out;
a60cd7
-				}
a60cd7
-				last_was_seek = 0;
a60cd7
-				goto adv;
a60cd7
-			}
a60cd7
-		}
a60cd7
-		/* sparse */
a60cd7
-		xlseek(dst_fd1, rd, SEEK_CUR);
a60cd7
-		if (dst_fd2 >= 0)
a60cd7
-			xlseek(dst_fd2, rd, SEEK_CUR);
a60cd7
-		last_was_seek = 1;
a60cd7
- adv:
a60cd7
-		total += rd;
a60cd7
-		size2 -= rd;
a60cd7
-		if (size2 < 0)
a60cd7
-			dst_fd2 = -1;
a60cd7
-// truncate to 0 or even delete the second file?
a60cd7
-// No, kernel does not delete nor truncate core files.
a60cd7
-	}
a60cd7
- out:
a60cd7
-
a60cd7
-#if CONFIG_FEATURE_COPYBUF_KB > 4
a60cd7
-	if (buffer_size != 4 * 1024)
a60cd7
-		munmap(buffer, buffer_size);
a60cd7
-#endif
a60cd7
-	return total;
a60cd7
-}
a60cd7
-
a60cd7
-
a60cd7
 /* Global data */
a60cd7
 static char *user_pwd;
a60cd7
 static DIR *proc_cwd;
a60cd7
@@ -609,13 +517,42 @@ static void create_core_backtrace(pid_t tid, const char *executable, int signal_
a60cd7
 #endif /* ENABLE_DUMP_TIME_UNWIND */
a60cd7
 }
a60cd7
 
a60cd7
+static ssize_t splice_entire_per_partes(int in_fd, int out_fd, size_t size_limit)
a60cd7
+{
a60cd7
+    size_t bytes = 0;
a60cd7
+    size_t soft_limit = KERNEL_PIPE_BUFFER_SIZE;
a60cd7
+    while (bytes < size_limit)
a60cd7
+    {
a60cd7
+        const size_t hard_limit = size_limit - bytes;
a60cd7
+        if (hard_limit < soft_limit)
a60cd7
+            soft_limit = hard_limit;
a60cd7
+
a60cd7
+        const ssize_t copied = splice(in_fd, NULL, out_fd, NULL, soft_limit, SPLICE_F_MOVE | SPLICE_F_MORE);
a60cd7
+        if (copied < 0)
a60cd7
+            return copied;
a60cd7
+
a60cd7
+        bytes += copied;
a60cd7
+
a60cd7
+        /* Check EOF. */
a60cd7
+        if (copied == 0)
a60cd7
+            break;
a60cd7
+    }
a60cd7
+
a60cd7
+    return bytes;
a60cd7
+}
a60cd7
+
a60cd7
+
a60cd7
 static int create_user_core(int user_core_fd, pid_t pid, off_t ulimit_c)
a60cd7
 {
a60cd7
     int err = 1;
a60cd7
     if (user_core_fd >= 0)
a60cd7
     {
a60cd7
-        off_t core_size = copyfd_size(STDIN_FILENO, user_core_fd, ulimit_c, COPYFD_SPARSE);
a60cd7
-        if (close_user_core(user_core_fd, core_size) != 0)
a60cd7
+        errno = 0;
a60cd7
+        ssize_t core_size = splice_entire_per_partes(STDIN_FILENO, user_core_fd, ulimit_c);
a60cd7
+        if (core_size < 0)
a60cd7
+            perror_msg("Failed to create user core '%s' in '%s'", core_basename, user_pwd);
a60cd7
+
a60cd7
+        if (close_user_core(user_core_fd, core_size) != 0 || core_size < 0)
a60cd7
             goto finito;
a60cd7
 
a60cd7
         err = 0;
a60cd7
@@ -732,6 +669,165 @@ static void error_msg_ignore_crash(const char *pid_str, const char *process_str,
a60cd7
     return;
a60cd7
 }
a60cd7
 
a60cd7
+static ssize_t splice_full(int in_fd, int out_fd, size_t size)
a60cd7
+{
a60cd7
+    ssize_t total = 0;
a60cd7
+    while (size != 0)
a60cd7
+    {
a60cd7
+        const ssize_t b = splice(in_fd, NULL, out_fd, NULL, size, 0);
a60cd7
+        if (b < 0)
a60cd7
+            return b;
a60cd7
+
a60cd7
+        if (b == 0)
a60cd7
+            break;
a60cd7
+
a60cd7
+        total += b;
a60cd7
+        size -= b;
a60cd7
+    }
a60cd7
+
a60cd7
+    return total;
a60cd7
+}
a60cd7
+
a60cd7
+static size_t xsplice_full(int in_fd, int out_fd, size_t size)
a60cd7
+{
a60cd7
+    const ssize_t r = splice_full(in_fd, out_fd, size);
a60cd7
+    if (r < 0)
a60cd7
+        perror_msg_and_die("Failed to write core dump to file");
a60cd7
+    return (size_t)r;
a60cd7
+}
a60cd7
+
a60cd7
+static void pipe_close(int *pfds)
a60cd7
+{
a60cd7
+    close(pfds[0]);
a60cd7
+    close(pfds[1]);
a60cd7
+    pfds[0] = pfds[1] = -1;
a60cd7
+}
a60cd7
+
a60cd7
+enum dump_core_files_ret_flags {
a60cd7
+    DUMP_ABRT_CORE_FAILED  = 0x0001,
a60cd7
+    DUMP_USER_CORE_FAILED  = 0x0100,
a60cd7
+};
a60cd7
+
a60cd7
+/* Optimized creation of two core files - ABRT and CWD
a60cd7
+ *
a60cd7
+ * The simplest optimization is to avoid the need to copy data to user space.
a60cd7
+ * In that case we cannot read data once and write them twice as we do with
a60cd7
+ * read/write approach because there is no syscall forwarding data from a
a60cd7
+ * single source fd to several destination fds (one might claim that there is
a60cd7
+ * tee() function but such a solution is suboptimal from our perspective).
a60cd7
+ *
a60cd7
+ * So the function first create ABRT core file and then creates user core file.
a60cd7
+ * If ABRT limit made the ABRT core to be smaller than allowed user core size,
a60cd7
+ * then the function reads more data from STDIN and appends them to the user
a60cd7
+ * core file.
a60cd7
+ *
a60cd7
+ * We must not read from the user core fd because that operation might be
a60cd7
+ * refused by OS.
a60cd7
+ */
a60cd7
+static int dump_two_core_files(int abrt_core_fd, size_t *abrt_limit, int user_core_fd, size_t *user_limit)
a60cd7
+{
a60cd7
+   /* tee() does not move the in_fd, thus you need to call splice to be
a60cd7
+    * get next chunk of data loaded into the in_fd buffer.
a60cd7
+    * So, calling tee() without splice() would be looping on the same
a60cd7
+    * data. Hence, we must ensure that after tee() we call splice() and
a60cd7
+    * that would be problematic if tee core limit is greater than splice
a60cd7
+    * core limit. Therefore, we swap the out fds based on their limits.
a60cd7
+    */
a60cd7
+    int    spliced_fd          = *abrt_limit > *user_limit ? abrt_core_fd    : user_core_fd;
a60cd7
+    size_t spliced_core_limit  = *abrt_limit > *user_limit ? *abrt_limit     : *user_limit;
a60cd7
+    int    teed_fd             = *abrt_limit > *user_limit ? user_core_fd    : abrt_core_fd;
a60cd7
+    size_t teed_core_limit     = *abrt_limit > *user_limit ? *user_limit     : *abrt_limit;
a60cd7
+
a60cd7
+    size_t *spliced_core_size  = *abrt_limit > *user_limit ? abrt_limit : user_limit;
a60cd7
+    size_t *teed_core_size     = *abrt_limit > *user_limit ? user_limit : abrt_limit;
a60cd7
+
a60cd7
+    *spliced_core_size = *teed_core_size = 0;
a60cd7
+
a60cd7
+    int cp[2] = { -1, -1 };
a60cd7
+    if (pipe(cp) < 0)
a60cd7
+    {
a60cd7
+        perror_msg("Failed to create temporary pipe for core file");
a60cd7
+        cp[0] = cp[1] = -1;
a60cd7
+    }
a60cd7
+
a60cd7
+    /* tee() can copy duplicate up to size of the pipe buffer bytes.
a60cd7
+     * It should not be problem to ask for more (in that case, tee would simply
a60cd7
+     * duplicate up to the limit bytes) but I would rather not to exceed
a60cd7
+     * the pipe buffer limit.
a60cd7
+     */
a60cd7
+    int copy_buffer_size = fcntl(STDIN_FILENO, F_GETPIPE_SZ);
a60cd7
+    if (copy_buffer_size < 0)
a60cd7
+        copy_buffer_size = KERNEL_PIPE_BUFFER_SIZE;
a60cd7
+
a60cd7
+    ssize_t to_write = copy_buffer_size;
a60cd7
+    for (;;)
a60cd7
+    {
a60cd7
+        if (cp[1] >= 0)
a60cd7
+        {
a60cd7
+            to_write = tee(STDIN_FILENO, cp[1], copy_buffer_size, 0);
a60cd7
+
a60cd7
+            /* Check EOF. */
a60cd7
+            if (to_write == 0)
a60cd7
+                break;
a60cd7
+
a60cd7
+            if (to_write < 0)
a60cd7
+            {
a60cd7
+                perror_msg("Cannot duplicate stdin buffer for core file");
a60cd7
+                pipe_close(cp);
a60cd7
+                to_write = copy_buffer_size;
a60cd7
+            }
a60cd7
+        }
a60cd7
+
a60cd7
+        size_t to_splice = to_write;
a60cd7
+        if (*spliced_core_size + to_splice > spliced_core_limit)
a60cd7
+            to_splice = spliced_core_limit - *spliced_core_size;
a60cd7
+
a60cd7
+        const size_t spliced = xsplice_full(STDIN_FILENO, spliced_fd, to_splice);
a60cd7
+        *spliced_core_size += spliced;
a60cd7
+
a60cd7
+        if (cp[0] >= 0)
a60cd7
+        {
a60cd7
+            size_t to_tee = to_write;
a60cd7
+            if (*teed_core_size + to_tee > teed_core_limit)
a60cd7
+                to_tee = teed_core_limit - *teed_core_size;
a60cd7
+
a60cd7
+            const ssize_t teed = splice_full(cp[0], teed_fd, to_tee);
a60cd7
+            if (teed < 0)
a60cd7
+            {
a60cd7
+                perror_msg("Cannot splice teed data to core file");
a60cd7
+                pipe_close(cp);
a60cd7
+                to_write = copy_buffer_size;
a60cd7
+            }
a60cd7
+            else
a60cd7
+                *teed_core_size += teed;
a60cd7
+
a60cd7
+            if (*teed_core_size >= teed_core_limit)
a60cd7
+            {
a60cd7
+                pipe_close(cp);
a60cd7
+                to_write = copy_buffer_size;
a60cd7
+            }
a60cd7
+        }
a60cd7
+
a60cd7
+        /* Check EOF. */
a60cd7
+        if (spliced == 0 || *spliced_core_size >= spliced_core_limit)
a60cd7
+            break;
a60cd7
+    }
a60cd7
+
a60cd7
+    int r = 0;
a60cd7
+    if (cp[0] < 0)
a60cd7
+    {
a60cd7
+        if (abrt_limit < user_limit)
a60cd7
+            r |= DUMP_ABRT_CORE_FAILED;
a60cd7
+        else
a60cd7
+            r |= DUMP_USER_CORE_FAILED;
a60cd7
+    }
a60cd7
+    else
a60cd7
+        pipe_close(cp);
a60cd7
+
a60cd7
+    return r;
a60cd7
+}
a60cd7
+
a60cd7
+
a60cd7
 int main(int argc, char** argv)
a60cd7
 {
a60cd7
     int err = 1;
a60cd7
@@ -755,6 +851,8 @@ int main(int argc, char** argv)
a60cd7
     bool setting_SaveBinaryImage;
a60cd7
     bool setting_SaveFullCore;
a60cd7
     bool setting_CreateCoreBacktrace;
a60cd7
+    unsigned int setting_MaxCoreFileSize = g_settings_nMaxCrashReportsSize;
a60cd7
+
a60cd7
     GList *setting_ignored_paths = NULL;
a60cd7
     GList *setting_allowed_users = NULL;
a60cd7
     GList *setting_allowed_groups = NULL;
a60cd7
@@ -780,6 +878,18 @@ int main(int argc, char** argv)
a60cd7
         if (value)
a60cd7
             setting_allowed_groups = parse_list(value);
a60cd7
 
a60cd7
+        value = get_map_string_item_or_NULL(settings, "MaxCoreFileSize");
a60cd7
+        if (value)
a60cd7
+        {
a60cd7
+            char *end;
a60cd7
+            errno = 0;
a60cd7
+            unsigned long ul = strtoul(value, &end, 10);
a60cd7
+            if (errno || end == value || *end != '\0' || ul > UINT_MAX)
a60cd7
+                error_msg("The MaxCoreFileSize option in the CCpp.conf file holds an invalid value");
a60cd7
+            else
a60cd7
+                setting_MaxCoreFileSize = ul;
a60cd7
+        }
a60cd7
+
a60cd7
         setting_CreateCoreBacktrace = value ? string_to_bool(value) : true;
a60cd7
         value = get_map_string_item_or_NULL(settings, "VerboseLog");
a60cd7
         if (value)
a60cd7
@@ -1019,8 +1129,8 @@ int main(int argc, char** argv)
a60cd7
 
a60cd7
         unlink(path);
a60cd7
         int abrt_core_fd = xopen3(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
a60cd7
-        off_t core_size = copyfd_eof(STDIN_FILENO, abrt_core_fd, COPYFD_SPARSE);
a60cd7
-        if (core_size < 0 || fsync(abrt_core_fd) != 0)
a60cd7
+        off_t core_size = splice_entire_per_partes(STDIN_FILENO, abrt_core_fd, SIZE_MAX);
a60cd7
+        if (core_size < 0 || fsync(abrt_core_fd) != 0 || close(abrt_core_fd) < 0)
a60cd7
         {
a60cd7
             unlink(path);
a60cd7
             /* copyfd_eof logs the error including errno string,
a60cd7
@@ -1133,31 +1243,58 @@ int main(int argc, char** argv)
a60cd7
             close(src_fd_binary);
a60cd7
         }
a60cd7
 
a60cd7
-        off_t core_size = 0;
a60cd7
+        size_t core_size = 0;
a60cd7
         if (setting_SaveFullCore)
a60cd7
         {
a60cd7
-            strcpy(path + path_len, "/"FILENAME_COREDUMP);
a60cd7
-            int abrt_core_fd = create_or_die(path, user_core_fd);
a60cd7
-
a60cd7
-            /* We write both coredumps at once.
a60cd7
-             * We can't write user coredump first, since it might be truncated
a60cd7
-             * and thus can't be copied and used as abrt coredump;
a60cd7
-             * and if we write abrt coredump first and then copy it as user one,
a60cd7
-             * then we have a race when process exits but coredump does not exist yet:
a60cd7
-             * $ echo -e '#include<signal.h>\nmain(){raise(SIGSEGV);}' | gcc -o test -x c -
a60cd7
-             * $ rm -f core*; ulimit -c unlimited; ./test; ls -l core*
a60cd7
-             * 21631 Segmentation fault (core dumped) ./test
a60cd7
-             * ls: cannot access core*: No such file or directory <=== BAD
a60cd7
-             */
a60cd7
-            core_size = copyfd_sparse(STDIN_FILENO, abrt_core_fd, user_core_fd, ulimit_c);
a60cd7
-            close_user_core(user_core_fd, core_size);
a60cd7
-            if (fsync(abrt_core_fd) != 0 || close(abrt_core_fd) != 0 || core_size < 0)
a60cd7
+            int abrt_core_fd = dd_open_item(dd, FILENAME_COREDUMP, O_RDWR);
a60cd7
+            if (abrt_core_fd < 0)
a60cd7
+            {   /* Avoid the need to deal with two destinations. */
a60cd7
+                perror_msg("Failed to create ABRT core file in '%s'", dd->dd_dirname);
a60cd7
+                create_user_core(user_core_fd, pid, ulimit_c);
a60cd7
+            }
a60cd7
+            else
a60cd7
             {
a60cd7
-                unlink(path);
a60cd7
-                dd_delete(dd);
a60cd7
-                /* copyfd_sparse logs the error including errno string,
a60cd7
-                 * but it does not log file name */
a60cd7
-                error_msg_and_die("Error writing '%s'", path);
a60cd7
+                size_t abrt_limit = 0;
a60cd7
+                if (   (g_settings_nMaxCrashReportsSize != 0 && setting_MaxCoreFileSize == 0)
a60cd7
+                    || (g_settings_nMaxCrashReportsSize != 0 && g_settings_nMaxCrashReportsSize < setting_MaxCoreFileSize))
a60cd7
+                    abrt_limit = g_settings_nMaxCrashReportsSize;
a60cd7
+                else
a60cd7
+                    abrt_limit = setting_MaxCoreFileSize;
a60cd7
+
a60cd7
+                if (abrt_limit != 0)
a60cd7
+                {
a60cd7
+                    const size_t abrt_limit_bytes = 1024 * 1024 * abrt_limit;
a60cd7
+                    /* Overflow protection. */
a60cd7
+                    if (abrt_limit_bytes > abrt_limit)
a60cd7
+                        abrt_limit = abrt_limit_bytes;
a60cd7
+                    else
a60cd7
+                    {
a60cd7
+                        error_msg("ABRT core file size limit (MaxCrashReportsSize|MaxCoreFileSize) does not fit into runtime type. Using maximal possible size.");
a60cd7
+                        abrt_limit = SIZE_MAX;
a60cd7
+                    }
a60cd7
+                }
a60cd7
+                else
a60cd7
+                    abrt_limit = SIZE_MAX;
a60cd7
+
a60cd7
+                if (user_core_fd < 0)
a60cd7
+                {
a60cd7
+                    const ssize_t r = splice_entire_per_partes(STDIN_FILENO, abrt_core_fd, abrt_limit);
a60cd7
+                    if (r < 0)
a60cd7
+                        perror_msg("Failed to write ABRT core file");
a60cd7
+                    else
a60cd7
+                        core_size = r;
a60cd7
+                }
a60cd7
+                else
a60cd7
+                {
a60cd7
+                    size_t user_limit = ulimit_c;
a60cd7
+                    const int r = dump_two_core_files(abrt_core_fd, &abrt_limit, user_core_fd, &user_limit);
a60cd7
+                    close_user_core(user_core_fd, (r & DUMP_USER_CORE_FAILED) ? -1 : user_limit);
a60cd7
+                    if (!(r & DUMP_ABRT_CORE_FAILED))
a60cd7
+                        core_size = abrt_limit;
a60cd7
+                }
a60cd7
+
a60cd7
+                if (fsync(abrt_core_fd) != 0 || close(abrt_core_fd) != 0)
a60cd7
+                    perror_msg("Failed to close ABRT core file");
a60cd7
             }
a60cd7
         }
a60cd7
         else
a60cd7
@@ -1223,8 +1360,8 @@ int main(int argc, char** argv)
a60cd7
         free(newpath);
a60cd7
 
a60cd7
         if (core_size > 0)
a60cd7
-            log_notice("Saved core dump of pid %lu (%s) to %s (%llu bytes)",
a60cd7
-                       (long)pid, executable, path, (long long)core_size);
a60cd7
+            log_notice("Saved core dump of pid %lu (%s) to %s (%zu bytes)",
a60cd7
+                       (long)pid, executable, path, core_size);
a60cd7
 
a60cd7
         notify_new_path(path);
a60cd7
 
a60cd7
-- 
a60cd7
2.17.2
a60cd7