Blob Blame History Raw
From 48ac9fdcc9d1e12a669d8664eeef1ed08c0f3969 Mon Sep 17 00:00:00 2001
From: Jakub Filak <jfilak@redhat.com>
Date: Wed, 21 Jan 2015 07:15:45 +0100
Subject: [PATCH] dump_dir: introduce dd_copy_file_unpack()

Related to abrt/abrt#548

Signed-off-by: Jakub Filak <jfilak@redhat.com>
---
 configure.ac                     |   1 +
 src/include/dump_dir.h           |   1 +
 src/include/internal_libreport.h |   5 ++
 src/lib/Makefile.am              |   3 +
 src/lib/compress.c               | 152 +++++++++++++++++++++++++++++++++++++++
 src/lib/dump_dir.c               |  17 +++++
 6 files changed, 179 insertions(+)
 create mode 100644 src/lib/compress.c

diff --git a/configure.ac b/configure.ac
index c925513..01d9f83 100644
--- a/configure.ac
+++ b/configure.ac
@@ -189,6 +189,7 @@ PKG_CHECK_MODULES([PROXY], [libproxy-1.0], [
 PKG_CHECK_MODULES([SATYR], [satyr])
 PKG_CHECK_MODULES([JOURNAL], [libsystemd-journal])
 PKG_CHECK_MODULES([AUGEAS], [augeas])
+PKG_CHECK_MODULES([LZMA], [liblzma])
 
 
 AC_ARG_WITH(newt,
diff --git a/src/include/dump_dir.h b/src/include/dump_dir.h
index 3a5ab36..fcd57c6 100644
--- a/src/include/dump_dir.h
+++ b/src/include/dump_dir.h
@@ -83,6 +83,7 @@ char* dd_load_text(const struct dump_dir *dd, const char *name);
 void dd_save_text(struct dump_dir *dd, const char *name, const char *data);
 void dd_save_binary(struct dump_dir *dd, const char *name, const char *data, unsigned size);
 int dd_copy_file(struct dump_dir *dd, const char *name, const char *source_path);
+int dd_copy_file_unpack(struct dump_dir *dd, const char *name, const char *source_path);
 /* Returns value less than 0 if any error occured; otherwise returns size of an
  * item in Bytes. If an item does not exist returns 0 instead of an error
  * value.
diff --git a/src/include/internal_libreport.h b/src/include/internal_libreport.h
index fca8138..7cda793 100644
--- a/src/include/internal_libreport.h
+++ b/src/include/internal_libreport.h
@@ -158,6 +158,11 @@ off_t copy_file(const char *src_name, const char *dst_name, int mode);
 #define copy_file_recursive libreport_copy_file_recursive
 int copy_file_recursive(const char *source, const char *dest);
 
+#define decompress_fd libreport_decompress_fd
+int decompress_fd(int fdi, int fdo);
+#define decompress_file libreport_decompress_file
+int decompress_file(const char *path_in, const char *path_out, mode_t mode_out);
+
 // NB: will return short read on error, not -1,
 // if some data was read before error occurred
 #define xread libreport_xread
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 2ec3f5a..4964da7 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -20,6 +20,7 @@ libreport_la_SOURCES = \
     logging.c \
     copyfd.c \
     copy_file_recursive.c \
+    compress.c \
     concat_path_file.c \
     append_to_malloced_string.c \
     overlapping_strcpy.c \
@@ -73,6 +74,7 @@ libreport_la_CPPFLAGS = \
     -DDUMP_DIR_OWNED_BY_USER=$(DUMP_DIR_OWNED_BY_USER) \
     -DLARGE_DATA_TMP_DIR=\"$(LARGE_DATA_TMP_DIR)\" \
     $(GLIB_CFLAGS) \
+    $(LZMA_CFLAGS) \
     $(GOBJECT_CFLAGS) \
     $(AUGEAS_CFLAGS) \
     -D_GNU_SOURCE
@@ -80,6 +82,7 @@ libreport_la_LDFLAGS = \
     -version-info 0:1:0
 libreport_la_LIBADD = \
     $(GLIB_LIBS) \
+    $(LZMA_LIBS) \
     $(JOURNAL_LIBS) \
     $(GOBJECT_LIBS) \
     $(AUGEAS_LIBS)
diff --git a/src/lib/compress.c b/src/lib/compress.c
new file mode 100644
index 0000000..eec155a
--- /dev/null
+++ b/src/lib/compress.c
@@ -0,0 +1,152 @@
+/*
+    Copyright (C) 2015  ABRT team
+    Copyright (C) 2015  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 "internal_libreport.h"
+
+#include <lzma.h>
+
+static const uint8_t s_xz_magic[6] = { 0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00 };
+
+static bool
+is_format(const char *name, const uint8_t *header, size_t hl, const uint8_t *magic, size_t ml)
+{
+    if (hl < ml)
+    {
+        log_warning("Too short header to detect '%s' file format.", name);
+        return false;
+    }
+
+    return memcmp(header, magic, ml) == 0;
+}
+
+static int
+decompress_fd_xz(int fdi, int fdo)
+{
+    uint8_t buf_in[BUFSIZ];
+    uint8_t buf_out[BUFSIZ];
+
+    lzma_stream strm = LZMA_STREAM_INIT;
+    lzma_ret ret = lzma_stream_decoder(&strm, UINT64_MAX, 0);
+    if (ret != LZMA_OK)
+    {
+        close(fdi);
+        close(fdo);
+        log_error("Failed to initialize XZ decoder: code %d", ret);
+        return -ENOMEM;
+    }
+
+    lzma_action action = LZMA_RUN;
+
+    strm.next_out = buf_out;
+    strm.avail_out = sizeof(buf_out);
+
+    for (;;)
+    {
+        if (strm.avail_in == 0 && action == LZMA_RUN)
+        {
+            strm.next_in = buf_in;
+            strm.avail_in = safe_read(fdi, buf_in, sizeof(buf_in));
+
+            if (strm.avail_in < 0)
+            {
+                perror_msg("Failed to read source core file");
+                close(fdi);
+                close(fdo);
+                lzma_end(&strm);
+                return -1;
+            }
+
+            if (strm.avail_in == 0)
+                action = LZMA_FINISH;
+        }
+
+        ret = lzma_code(&strm, action);
+
+        if (strm.avail_out == 0 || ret == LZMA_STREAM_END)
+        {
+            const ssize_t n = sizeof(buf_out) - strm.avail_out;
+            if (n != safe_write(fdo, buf_out, n))
+            {
+                perror_msg("Failed to write decompressed data");
+                close(fdi);
+                close(fdo);
+                lzma_end(&strm);
+                return -1;
+            }
+
+            if (ret == LZMA_STREAM_END)
+            {
+                log_debug("Successfully decompressed coredump.");
+                break;
+            }
+
+            strm.next_out = buf_out;
+            strm.avail_out = sizeof(buf_out);
+        }
+    }
+
+    return 0;
+}
+
+int
+decompress_fd(int fdi, int fdo)
+{
+    uint8_t header[6];
+
+    if (sizeof(header) != safe_read(fdi, header, sizeof(header)))
+    {
+        perror_msg("Failed to read header bytes");
+        return -1;
+    }
+
+    xlseek(fdi, 0, SEEK_SET);
+
+    if (is_format("xz", header, sizeof(header), s_xz_magic, sizeof(s_xz_magic)))
+        return decompress_fd_xz(fdi, fdo);
+
+    error_msg("Unsupported file format");
+    return -1;
+}
+
+int
+decompress_file(const char *path_in, const char *path_out, mode_t mode_out)
+{
+    int fdi = open(path_in, O_RDONLY | O_CLOEXEC);
+    if (fdi < 0)
+    {
+        perror_msg("Could not open file: %s", path_in);
+        return -1;
+    }
+
+    int fdo = open(path_out, O_WRONLY | O_CLOEXEC | O_EXCL | O_CREAT, mode_out);
+    if (fdo < 0)
+    {
+        close(fdi);
+        perror_msg("Could not create file: %s", path_out);
+        return -1;
+    }
+
+    int ret = decompress_fd(fdi, fdo);
+    close(fdi);
+    close(fdo);
+
+    if (ret != 0)
+        unlink(path_out);
+
+    return ret;
+}
diff --git a/src/lib/dump_dir.c b/src/lib/dump_dir.c
index 0dea02e..d50ebf7 100644
--- a/src/lib/dump_dir.c
+++ b/src/lib/dump_dir.c
@@ -1314,3 +1314,20 @@ int dd_copy_file(struct dump_dir *dd, const char *name, const char *source_path)
     free(dest);
     return copied < 0;
 }
+
+int dd_copy_file_unpack(struct dump_dir *dd, const char *name, const char *source_path)
+{
+    char *dest = concat_path_file(dd->dd_dirname, name);
+
+    log_debug("unpacking '%s' to '%s'", source_path, dest);
+
+    off_t copied = decompress_file(source_path, dest, DEFAULT_DUMP_DIR_MODE | S_IROTH);
+    if (copied != 0)
+        error_msg("Can't copy %s to %s", source_path, dest);
+    else
+        log_debug("unpackaged file '%s'", dest);
+
+    free(dest);
+    return copied < 0;
+
+}
-- 
2.1.0