blob: 5bfccb789ec0e1ca98fa49e3ffb1bc11964b61bf [file] [log] [blame]
#include <unistd.h>
#include <sys/types.h>
#include "kexec-lzma.h"
#include "config.h"
#include "kexec.h"
#ifdef HAVE_LIBLZMA
#define _GNU_SOURCE
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <sys/stat.h>
#include <ctype.h>
#include <lzma.h>
#define kBufferSize (1 << 15)
typedef struct lzfile {
uint8_t buf[kBufferSize];
lzma_stream strm;
FILE *file;
int encoding;
int eof;
} LZFILE;
LZFILE *lzopen(const char *path, const char *mode);
int lzclose(LZFILE *lzfile);
ssize_t lzread(LZFILE *lzfile, void *buf, size_t len);
static LZFILE *lzopen_internal(const char *path, const char *mode, int fd)
{
int level = 5;
int encoding = 0;
FILE *fp;
LZFILE *lzfile;
lzma_ret ret;
lzma_stream lzma_strm_tmp = LZMA_STREAM_INIT;
for (; *mode; mode++) {
if (*mode == 'w')
encoding = 1;
else if (*mode == 'r')
encoding = 0;
else if (*mode >= '1' && *mode <= '9')
level = *mode - '0';
}
if (fd != -1)
fp = fdopen(fd, encoding ? "w" : "r");
else
fp = fopen(path, encoding ? "w" : "r");
if (!fp)
return NULL;
lzfile = calloc(1, sizeof(*lzfile));
if (!lzfile) {
fclose(fp);
return NULL;
}
lzfile->file = fp;
lzfile->encoding = encoding;
lzfile->eof = 0;
lzfile->strm = lzma_strm_tmp;
if (encoding) {
lzma_options_lzma opt_lzma;
if (lzma_lzma_preset(&opt_lzma, level - 1))
return NULL;
ret = lzma_alone_encoder(&lzfile->strm, &opt_lzma);
} else {
ret = lzma_auto_decoder(&lzfile->strm,
UINT64_C(64) * 1024 * 1024, 0);
}
if (ret != LZMA_OK) {
fclose(fp);
free(lzfile);
return NULL;
}
return lzfile;
}
LZFILE *lzopen(const char *path, const char *mode)
{
return lzopen_internal(path, mode, -1);
}
int lzclose(LZFILE *lzfile)
{
lzma_ret ret;
size_t n;
if (!lzfile)
return -1;
if (lzfile->encoding) {
for (;;) {
lzfile->strm.avail_out = kBufferSize;
lzfile->strm.next_out = lzfile->buf;
ret = lzma_code(&lzfile->strm, LZMA_FINISH);
if (ret != LZMA_OK && ret != LZMA_STREAM_END)
return -1;
n = kBufferSize - lzfile->strm.avail_out;
if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
return -1;
if (ret == LZMA_STREAM_END)
break;
}
}
lzma_end(&lzfile->strm);
return fclose(lzfile->file);
free(lzfile);
}
ssize_t lzread(LZFILE *lzfile, void *buf, size_t len)
{
lzma_ret ret;
int eof = 0;
if (!lzfile || lzfile->encoding)
return -1;
if (lzfile->eof)
return 0;
lzfile->strm.next_out = buf;
lzfile->strm.avail_out = len;
for (;;) {
if (!lzfile->strm.avail_in) {
lzfile->strm.next_in = lzfile->buf;
lzfile->strm.avail_in = fread(lzfile->buf, 1, kBufferSize, lzfile->file);
if (!lzfile->strm.avail_in)
eof = 1;
}
ret = lzma_code(&lzfile->strm, LZMA_RUN);
if (ret == LZMA_STREAM_END) {
lzfile->eof = 1;
return len - lzfile->strm.avail_out;
}
if (ret != LZMA_OK)
return -1;
if (!lzfile->strm.avail_out)
return len;
if (eof)
return -1;
}
}
char *lzma_decompress_file(const char *filename, off_t *r_size)
{
LZFILE *fp;
char *buf;
off_t size, allocated;
ssize_t result;
dbgprintf("Try LZMA decompression.\n");
*r_size = 0;
if (!filename)
return NULL;
fp = lzopen(filename, "rb");
if (fp == 0) {
dbgprintf("Cannot open `%s'\n", filename);
return NULL;
}
size = 0;
allocated = 65536;
buf = xmalloc(allocated);
do {
if (size == allocated) {
allocated <<= 1;
buf = xrealloc(buf, allocated);
}
result = lzread(fp, buf + size, allocated - size);
if (result < 0) {
if ((errno == EINTR) || (errno == EAGAIN))
continue;
dbgprintf("%s: read on %s of %ld bytes failed\n",
__func__, filename, (allocated - size) + 0UL);
break;
}
size += result;
} while (result > 0);
if (lzclose(fp) != LZMA_OK) {
dbgprintf("%s: Close of %s failed\n", __func__, filename);
goto fail;
}
if (result < 0)
goto fail;
*r_size = size;
return buf;
fail:
free(buf);
return NULL;
}
#else
char *lzma_decompress_file(const char *UNUSED(filename), off_t *UNUSED(r_size))
{
return NULL;
}
#endif /* HAVE_LIBLZMA */