162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <errno.h>
362306a36Sopenharmony_ci#include <lzma.h>
462306a36Sopenharmony_ci#include <stdio.h>
562306a36Sopenharmony_ci#include <linux/compiler.h>
662306a36Sopenharmony_ci#include <sys/types.h>
762306a36Sopenharmony_ci#include <sys/stat.h>
862306a36Sopenharmony_ci#include <fcntl.h>
962306a36Sopenharmony_ci#include "compress.h"
1062306a36Sopenharmony_ci#include "debug.h"
1162306a36Sopenharmony_ci#include <string.h>
1262306a36Sopenharmony_ci#include <unistd.h>
1362306a36Sopenharmony_ci#include <internal/lib.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define BUFSIZE 8192
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic const char *lzma_strerror(lzma_ret ret)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	switch ((int) ret) {
2062306a36Sopenharmony_ci	case LZMA_MEM_ERROR:
2162306a36Sopenharmony_ci		return "Memory allocation failed";
2262306a36Sopenharmony_ci	case LZMA_OPTIONS_ERROR:
2362306a36Sopenharmony_ci		return "Unsupported decompressor flags";
2462306a36Sopenharmony_ci	case LZMA_FORMAT_ERROR:
2562306a36Sopenharmony_ci		return "The input is not in the .xz format";
2662306a36Sopenharmony_ci	case LZMA_DATA_ERROR:
2762306a36Sopenharmony_ci		return "Compressed file is corrupt";
2862306a36Sopenharmony_ci	case LZMA_BUF_ERROR:
2962306a36Sopenharmony_ci		return "Compressed file is truncated or otherwise corrupt";
3062306a36Sopenharmony_ci	default:
3162306a36Sopenharmony_ci		return "Unknown error, possibly a bug";
3262306a36Sopenharmony_ci	}
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ciint lzma_decompress_to_file(const char *input, int output_fd)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	lzma_action action = LZMA_RUN;
3862306a36Sopenharmony_ci	lzma_stream strm   = LZMA_STREAM_INIT;
3962306a36Sopenharmony_ci	lzma_ret ret;
4062306a36Sopenharmony_ci	int err = -1;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	u8 buf_in[BUFSIZE];
4362306a36Sopenharmony_ci	u8 buf_out[BUFSIZE];
4462306a36Sopenharmony_ci	FILE *infile;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	infile = fopen(input, "rb");
4762306a36Sopenharmony_ci	if (!infile) {
4862306a36Sopenharmony_ci		pr_debug("lzma: fopen failed on %s: '%s'\n", input, strerror(errno));
4962306a36Sopenharmony_ci		return -1;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	ret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED);
5362306a36Sopenharmony_ci	if (ret != LZMA_OK) {
5462306a36Sopenharmony_ci		pr_debug("lzma: lzma_stream_decoder failed %s (%d)\n", lzma_strerror(ret), ret);
5562306a36Sopenharmony_ci		goto err_fclose;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	strm.next_in   = NULL;
5962306a36Sopenharmony_ci	strm.avail_in  = 0;
6062306a36Sopenharmony_ci	strm.next_out  = buf_out;
6162306a36Sopenharmony_ci	strm.avail_out = sizeof(buf_out);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	while (1) {
6462306a36Sopenharmony_ci		if (strm.avail_in == 0 && !feof(infile)) {
6562306a36Sopenharmony_ci			strm.next_in  = buf_in;
6662306a36Sopenharmony_ci			strm.avail_in = fread(buf_in, 1, sizeof(buf_in), infile);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci			if (ferror(infile)) {
6962306a36Sopenharmony_ci				pr_debug("lzma: read error: %s\n", strerror(errno));
7062306a36Sopenharmony_ci				goto err_lzma_end;
7162306a36Sopenharmony_ci			}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci			if (feof(infile))
7462306a36Sopenharmony_ci				action = LZMA_FINISH;
7562306a36Sopenharmony_ci		}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci		ret = lzma_code(&strm, action);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci		if (strm.avail_out == 0 || ret == LZMA_STREAM_END) {
8062306a36Sopenharmony_ci			ssize_t write_size = sizeof(buf_out) - strm.avail_out;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci			if (writen(output_fd, buf_out, write_size) != write_size) {
8362306a36Sopenharmony_ci				pr_debug("lzma: write error: %s\n", strerror(errno));
8462306a36Sopenharmony_ci				goto err_lzma_end;
8562306a36Sopenharmony_ci			}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci			strm.next_out  = buf_out;
8862306a36Sopenharmony_ci			strm.avail_out = sizeof(buf_out);
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		if (ret != LZMA_OK) {
9262306a36Sopenharmony_ci			if (ret == LZMA_STREAM_END)
9362306a36Sopenharmony_ci				break;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci			pr_debug("lzma: failed %s\n", lzma_strerror(ret));
9662306a36Sopenharmony_ci			goto err_lzma_end;
9762306a36Sopenharmony_ci		}
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	err = 0;
10162306a36Sopenharmony_cierr_lzma_end:
10262306a36Sopenharmony_ci	lzma_end(&strm);
10362306a36Sopenharmony_cierr_fclose:
10462306a36Sopenharmony_ci	fclose(infile);
10562306a36Sopenharmony_ci	return err;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cibool lzma_is_compressed(const char *input)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	int fd = open(input, O_RDONLY);
11162306a36Sopenharmony_ci	const uint8_t magic[6] = { 0xFD, '7', 'z', 'X', 'Z', 0x00 };
11262306a36Sopenharmony_ci	char buf[6] = { 0 };
11362306a36Sopenharmony_ci	ssize_t rc;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (fd < 0)
11662306a36Sopenharmony_ci		return -1;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	rc = read(fd, buf, sizeof(buf));
11962306a36Sopenharmony_ci	close(fd);
12062306a36Sopenharmony_ci	return rc == sizeof(buf) ?
12162306a36Sopenharmony_ci	       memcmp(buf, magic, sizeof(buf)) == 0 : false;
12262306a36Sopenharmony_ci}
123