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