1/*
2 * Copyright (c) 2010
3 * Phillip Lougher <phillip@lougher.demon.co.uk>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2,
8 * or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 * lzma_xz_wrapper.c
20 *
21 * Support for LZMA1 compression using XZ Utils liblzma http://tukaani.org/xz/
22 */
23
24#include <stdio.h>
25#include <string.h>
26#include <lzma.h>
27
28#include "squashfs_fs.h"
29#include "compressor.h"
30
31#define LZMA_PROPS_SIZE 5
32#define LZMA_UNCOMP_SIZE 8
33#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + LZMA_UNCOMP_SIZE)
34
35#define LZMA_OPTIONS 5
36#define MEMLIMIT (32 * 1024 * 1024)
37
38static int lzma_compress(void *dummy, void *dest, void *src,  int size,
39	int block_size, int *error)
40{
41	unsigned char *d = (unsigned char *) dest;
42	lzma_options_lzma opt;
43	lzma_stream strm = LZMA_STREAM_INIT;
44	int res;
45
46	lzma_lzma_preset(&opt, LZMA_OPTIONS);
47	opt.dict_size = block_size;
48
49	res = lzma_alone_encoder(&strm, &opt);
50	if(res != LZMA_OK) {
51		lzma_end(&strm);
52		goto failed;
53	}
54
55	strm.next_out = dest;
56	strm.avail_out = block_size;
57	strm.next_in = src;
58	strm.avail_in = size;
59
60	res = lzma_code(&strm, LZMA_FINISH);
61	lzma_end(&strm);
62
63	if(res == LZMA_STREAM_END) {
64		/*
65	 	 * Fill in the 8 byte little endian uncompressed size field in
66		 * the LZMA header.  8 bytes is excessively large for squashfs
67		 * but this is the standard LZMA header and which is expected by
68		 * the kernel code
69	 	 */
70
71		d[LZMA_PROPS_SIZE] = size & 255;
72		d[LZMA_PROPS_SIZE + 1] = (size >> 8) & 255;
73		d[LZMA_PROPS_SIZE + 2] = (size >> 16) & 255;
74		d[LZMA_PROPS_SIZE + 3] = (size >> 24) & 255;
75		d[LZMA_PROPS_SIZE + 4] = 0;
76		d[LZMA_PROPS_SIZE + 5] = 0;
77		d[LZMA_PROPS_SIZE + 6] = 0;
78		d[LZMA_PROPS_SIZE + 7] = 0;
79
80		return (int) strm.total_out;
81	}
82
83	if(res == LZMA_OK)
84		/*
85	 	 * Output buffer overflow.  Return out of buffer space
86	 	 */
87		return 0;
88
89failed:
90	/*
91	 * All other errors return failure, with the compressor
92	 * specific error code in *error
93	 */
94	*error = res;
95	return -1;
96}
97
98
99static int lzma_uncompress(void *dest, void *src, int size, int block_size,
100	int *error)
101{
102	lzma_stream strm = LZMA_STREAM_INIT;
103	int uncompressed_size = 0, res;
104	unsigned char lzma_header[LZMA_HEADER_SIZE];
105
106	res = lzma_alone_decoder(&strm, MEMLIMIT);
107	if(res != LZMA_OK) {
108		lzma_end(&strm);
109		goto failed;
110	}
111
112	memcpy(lzma_header, src, LZMA_HEADER_SIZE);
113	uncompressed_size = lzma_header[LZMA_PROPS_SIZE] |
114		(lzma_header[LZMA_PROPS_SIZE + 1] << 8) |
115		(lzma_header[LZMA_PROPS_SIZE + 2] << 16) |
116		(lzma_header[LZMA_PROPS_SIZE + 3] << 24);
117	memset(lzma_header + LZMA_PROPS_SIZE, 255, LZMA_UNCOMP_SIZE);
118
119	strm.next_out = dest;
120	strm.avail_out = block_size;
121	strm.next_in = lzma_header;
122	strm.avail_in = LZMA_HEADER_SIZE;
123
124	res = lzma_code(&strm, LZMA_RUN);
125
126	if(res != LZMA_OK || strm.avail_in != 0) {
127		lzma_end(&strm);
128		goto failed;
129	}
130
131	strm.next_in = src + LZMA_HEADER_SIZE;
132	strm.avail_in = size - LZMA_HEADER_SIZE;
133
134	res = lzma_code(&strm, LZMA_FINISH);
135	lzma_end(&strm);
136
137	if(res == LZMA_STREAM_END || (res == LZMA_OK &&
138		strm.total_out >= uncompressed_size && strm.avail_in == 0))
139		return uncompressed_size;
140
141failed:
142	*error = res;
143	return -1;
144}
145
146
147struct compressor lzma_comp_ops = {
148	.init = NULL,
149	.compress = lzma_compress,
150	.uncompress = lzma_uncompress,
151	.options = NULL,
152	.usage = NULL,
153	.id = LZMA_COMPRESSION,
154	.name = "lzma",
155	.supported = 1
156};
157